| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package XS::JIT::Builder; |
|
2
|
|
|
|
|
|
|
|
|
3
|
46
|
|
|
46
|
|
2894708
|
use strict; |
|
|
46
|
|
|
|
|
68
|
|
|
|
46
|
|
|
|
|
2938
|
|
|
4
|
46
|
|
|
46
|
|
1651
|
use warnings; |
|
|
46
|
|
|
|
|
1616
|
|
|
|
46
|
|
|
|
|
4109
|
|
|
5
|
46
|
|
|
46
|
|
652
|
use 5.010; |
|
|
46
|
|
|
|
|
117
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.17'; |
|
8
|
|
|
|
|
|
|
|
|
9
|
46
|
|
|
46
|
|
214
|
use Exporter 'import'; |
|
|
46
|
|
|
|
|
84
|
|
|
|
46
|
|
|
|
|
20618
|
|
|
10
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
|
11
|
|
|
|
|
|
|
INLINE_NONE INLINE_GETTER INLINE_SETTER |
|
12
|
|
|
|
|
|
|
INLINE_HV_GETTER INLINE_HV_SETTER |
|
13
|
|
|
|
|
|
|
TYPE_ANY TYPE_DEFINED TYPE_INT TYPE_NUM TYPE_STR |
|
14
|
|
|
|
|
|
|
TYPE_REF TYPE_ARRAYREF TYPE_HASHREF TYPE_CODEREF |
|
15
|
|
|
|
|
|
|
TYPE_OBJECT TYPE_BLESSED |
|
16
|
|
|
|
|
|
|
); |
|
17
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
|
18
|
|
|
|
|
|
|
inline => [qw(INLINE_NONE INLINE_GETTER INLINE_SETTER |
|
19
|
|
|
|
|
|
|
INLINE_HV_GETTER INLINE_HV_SETTER)], |
|
20
|
|
|
|
|
|
|
types => [qw(TYPE_ANY TYPE_DEFINED TYPE_INT TYPE_NUM TYPE_STR |
|
21
|
|
|
|
|
|
|
TYPE_REF TYPE_ARRAYREF TYPE_HASHREF TYPE_CODEREF |
|
22
|
|
|
|
|
|
|
TYPE_OBJECT TYPE_BLESSED)], |
|
23
|
|
|
|
|
|
|
); |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
require XSLoader; |
|
26
|
|
|
|
|
|
|
XSLoader::load('XS::JIT::Builder', $VERSION); |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub write_file { |
|
29
|
0
|
|
|
0
|
1
|
|
my ($self, $filename) = @_; |
|
30
|
|
|
|
|
|
|
|
|
31
|
0
|
0
|
|
|
|
|
die "write_file requires a filename" unless defined $filename; |
|
32
|
|
|
|
|
|
|
|
|
33
|
0
|
0
|
|
|
|
|
open my $fh, '>', $filename |
|
34
|
|
|
|
|
|
|
or die "Cannot open '$filename' for writing: $!"; |
|
35
|
0
|
|
|
|
|
|
print $fh $self->code; |
|
36
|
0
|
0
|
|
|
|
|
close $fh |
|
37
|
|
|
|
|
|
|
or die "Cannot close '$filename': $!"; |
|
38
|
|
|
|
|
|
|
|
|
39
|
0
|
|
|
|
|
|
return $self; |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 NAME |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
XS::JIT::Builder - interface for building XS/C code strings |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
use XS::JIT; |
|
49
|
|
|
|
|
|
|
use XS::JIT::Builder; |
|
50
|
|
|
|
|
|
|
use File::Temp qw(tempdir); |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my $cache_dir = tempdir(CLEANUP => 1); |
|
53
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# Build a simple accessor |
|
56
|
|
|
|
|
|
|
$b->xs_function('get_name') |
|
57
|
|
|
|
|
|
|
->xs_preamble |
|
58
|
|
|
|
|
|
|
->get_self_hv |
|
59
|
|
|
|
|
|
|
->hv_fetch('hv', 'name', 4, 'val') |
|
60
|
|
|
|
|
|
|
->if('val != NULL') |
|
61
|
|
|
|
|
|
|
->return_sv('*val') |
|
62
|
|
|
|
|
|
|
->endif |
|
63
|
|
|
|
|
|
|
->return_undef |
|
64
|
|
|
|
|
|
|
->xs_end; |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# Compile and use |
|
67
|
|
|
|
|
|
|
XS::JIT->compile( |
|
68
|
|
|
|
|
|
|
code => $b->code, |
|
69
|
|
|
|
|
|
|
name => 'MyClass', |
|
70
|
|
|
|
|
|
|
cache_dir => $cache_dir, |
|
71
|
|
|
|
|
|
|
functions => { |
|
72
|
|
|
|
|
|
|
'MyClass::name' => { source => 'get_name', is_xs_native => 1 }, |
|
73
|
|
|
|
|
|
|
}, |
|
74
|
|
|
|
|
|
|
); |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
my $obj = bless { name => 'Alice' }, 'MyClass'; |
|
77
|
|
|
|
|
|
|
print $obj->name; # prints "Alice" |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
This module is experimental, the api and the code generated will evolve over time. |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
XS::JIT::Builder provides a interface for generating C code dynamically. |
|
84
|
|
|
|
|
|
|
It handles indentation automatically and provides convenient methods for |
|
85
|
|
|
|
|
|
|
common XS patterns. |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
All methods return C<$self> to enable method chaining. The generated code |
|
88
|
|
|
|
|
|
|
can be retrieved with C and passed to Ccompile()>. |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head1 METHODS |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=head2 Lifecycle |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=over 4 |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=item new(%options) |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Create a new builder. |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
101
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new(use_tabs => 1); |
|
102
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new(indent_width => 2); |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
Options: |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=over 4 |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=item use_tabs |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
Use tabs for indentation instead of spaces (default: false). |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=item indent_width |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
Number of spaces per indentation level when not using tabs (default: 4). |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=back |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=item code() |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Return the accumulated C code as a string. |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
my $c_code = $b->code; |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=item reset() |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
Clear the builder and reset state for reuse. |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
$b->reset; |
|
129
|
|
|
|
|
|
|
# Builder is now empty, can build new code |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=item write_file($filename) |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Write the accumulated C code to a file. This is useful for: |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=over 4 |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=item * Distribution - Generate the XS file once, ship it with your module |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=item * Debugging - Inspect the generated code |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item * Performance - Skip runtime JIT compilation entirely |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=back |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
$b->write_file('MyModule.c'); |
|
146
|
|
|
|
|
|
|
# Now you can compile MyModule.c as a regular XS/C extension |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
Returns C<$self> to enable method chaining. |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=back |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head2 Low-level Output |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=over 4 |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=item line($text) |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Add a line with current indentation. |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
$b->line('int x = 42;'); |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=item raw($text) |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Add raw text without automatic indentation or newline. |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
$b->raw('/* inline comment */'); |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=item comment($text) |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Add a C comment. |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
$b->comment('This is a comment'); |
|
173
|
|
|
|
|
|
|
# Generates: /* This is a comment */ |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=item blank() |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Add a blank line. |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=back |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=head2 XS Structure |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=over 4 |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item xs_function($name) |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
Start an XS function definition. |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
$b->xs_function('my_function'); |
|
190
|
|
|
|
|
|
|
# Generates: XS_EUPXS(my_function) { |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=item xs_preamble() |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
Add standard XS preamble (dVAR, dXSARGS, etc.). |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$b->xs_function('my_func') |
|
197
|
|
|
|
|
|
|
->xs_preamble; |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=item xs_end() |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
Close an XS function. |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
$b->xs_end; |
|
204
|
|
|
|
|
|
|
# Generates: } |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=item xs_return($count) |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Add XSRETURN statement. |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
$b->xs_return('1'); |
|
211
|
|
|
|
|
|
|
# Generates: XSRETURN(1); |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=item xs_return_undef() |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Return undef from XS function. |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
$b->xs_return_undef; |
|
218
|
|
|
|
|
|
|
# Generates: ST(0) = &PL_sv_undef; XSRETURN(1); |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=item method_start($name, $min_args, $max_args, $usage) |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Start a method with argument checking. |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
$b->method_start('set_value', 2, 2, '$self, $value'); |
|
225
|
|
|
|
|
|
|
# Generates function with items check and usage croak |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=back |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=head2 Control Flow |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=over 4 |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=item if($condition) |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Start an if block. |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
$b->if('items > 1'); |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=item elsif($condition) |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
Add an elsif clause. |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
$b->elsif('items == 1'); |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=item else() |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Add an else clause. |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
$b->else; |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=item endif() |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
Close an if/elsif/else block. |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
$b->if('x > 0') |
|
256
|
|
|
|
|
|
|
->return_pv('positive') |
|
257
|
|
|
|
|
|
|
->endif; |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item for($init, $cond, $incr) |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Start a for loop. |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
$b->for('int i = 0', 'i < 10', 'i++'); |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=item while($condition) |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Start a while loop. |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
$b->while('ptr != NULL'); |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item endloop() / endfor() / endwhile() |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Close a loop. All three are aliases. |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
$b->for('int i = 0', 'i < n', 'i++') |
|
276
|
|
|
|
|
|
|
->raw('sum += array[i];') |
|
277
|
|
|
|
|
|
|
->endfor; |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=item block() |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Start a bare block. |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
$b->block; |
|
284
|
|
|
|
|
|
|
# Generates: { |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=item endblock() |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
Close a bare block. |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
$b->block |
|
291
|
|
|
|
|
|
|
->declare_iv('temp', 'x') |
|
292
|
|
|
|
|
|
|
->raw('x = y; y = temp;') |
|
293
|
|
|
|
|
|
|
->endblock; |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=back |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head2 Variable Declarations |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=over 4 |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=item declare($type, $name, $value) |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
Declare a variable of any type. |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
$b->declare('int', 'count', '0'); |
|
306
|
|
|
|
|
|
|
# Generates: int count = 0; |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=item declare_sv($name, $value) |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
Declare an SV* variable. |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
$b->declare_sv('arg', 'ST(1)'); |
|
313
|
|
|
|
|
|
|
# Generates: SV* arg = ST(1); |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
=item declare_iv($name, $value) |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
Declare an IV (integer) variable. |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
$b->declare_iv('count', 'items - 1'); |
|
320
|
|
|
|
|
|
|
# Generates: IV count = items - 1; |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=item declare_nv($name, $value) |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
Declare an NV (double) variable. |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
$b->declare_nv('total', '0.0'); |
|
327
|
|
|
|
|
|
|
# Generates: NV total = 0.0; |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=item declare_pv($name, $value) |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Declare a const char* variable. |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
$b->declare_pv('str', 'SvPV_nolen(ST(0))'); |
|
334
|
|
|
|
|
|
|
# Generates: const char* str = SvPV_nolen(ST(0)); |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=item declare_hv($name, $value) |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Declare an HV* variable. |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
$b->declare_hv('hash', '(HV*)SvRV(arg)'); |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=item declare_av($name, $value) |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
Declare an AV* variable. |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
$b->declare_av('array', '(AV*)SvRV(arg)'); |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=item new_hv($name) |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Declare and create a new hash. |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
$b->new_hv('hv'); |
|
353
|
|
|
|
|
|
|
# Generates: HV* hv = newHV(); |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=item new_av($name) |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Declare and create a new array. |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
$b->new_av('results'); |
|
360
|
|
|
|
|
|
|
# Generates: AV* results = newAV(); |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
=item assign($var, $value) |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
Assign a value to an existing variable. |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
$b->assign('count', 'count + 1'); |
|
367
|
|
|
|
|
|
|
# Generates: count = count + 1; |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=back |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head2 Type Checking |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=over 4 |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=item check_items($min, $max, $usage) |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
Check argument count, croak if wrong. Use max=-1 for no upper limit. |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
$b->check_items(1, 2, '$self, $value?'); |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=item check_defined($sv, $error_msg) |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
Croak if SV is not defined. |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
$b->check_defined('ST(1)', 'value required'); |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
=item check_hashref($sv, $error_msg) |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
Croak if SV is not a hash reference. |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
$b->check_hashref('ST(0)', 'expected hashref'); |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=item check_arrayref($sv, $error_msg) |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
Croak if SV is not an array reference. |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
$b->check_arrayref('arg', 'expected arrayref'); |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=back |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=head2 SV Conversion |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=over 4 |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=item sv_to_iv($result_var, $sv) |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
Convert SV to integer. |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
$b->sv_to_iv('count', 'ST(1)'); |
|
410
|
|
|
|
|
|
|
# Generates: IV count = SvIV(ST(1)); |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=item sv_to_nv($result_var, $sv) |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
Convert SV to double. |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
$b->sv_to_nv('value', 'ST(1)'); |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=item sv_to_pv($result_var, $len_var, $sv) |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
Convert SV to string with length. |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
$b->sv_to_pv('str', 'len', 'ST(1)'); |
|
423
|
|
|
|
|
|
|
# Generates: STRLEN len; const char* str = SvPV(ST(1), len); |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=item sv_to_bool($result_var, $sv) |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
Convert SV to boolean. |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
$b->sv_to_bool('flag', 'ST(1)'); |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=item new_sv_iv($result_var, $value) |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
Create new SV from integer. |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
$b->new_sv_iv('result', 'count'); |
|
436
|
|
|
|
|
|
|
# Generates: SV* result = newSViv(count); |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=item new_sv_nv($result_var, $value) |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
Create new SV from double. |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
$b->new_sv_nv('result', 'total'); |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=item new_sv_pv($result_var, $str, $len) |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
Create new SV from string. |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
$b->new_sv_pv('result', 'buffer', 'len'); |
|
449
|
|
|
|
|
|
|
# Generates: SV* result = newSVpvn(buffer, len); |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=back |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=head2 Return Helpers |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=over 4 |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=item return_iv($value) |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
Return an integer value. |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
$b->return_iv('42'); |
|
462
|
|
|
|
|
|
|
$b->return_iv('count'); |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=item return_nv($value) |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
Return a double value. |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
$b->return_nv('3.14'); |
|
469
|
|
|
|
|
|
|
$b->return_nv('result'); |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=item return_pv($str) |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
Return a string value. The string is automatically quoted if it doesn't |
|
474
|
|
|
|
|
|
|
start with a letter/underscore (i.e., if it's not a C variable name). |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
$b->return_pv('hello'); # Returns literal "hello" |
|
477
|
|
|
|
|
|
|
$b->return_pv('buffer'); # Returns C variable buffer |
|
478
|
|
|
|
|
|
|
$b->return_pv('"quoted"'); # Returns literal "quoted" |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=item return_sv($sv) |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Return an SV. |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
$b->return_sv('result'); |
|
485
|
|
|
|
|
|
|
$b->return_sv('sv_2mortal(newSViv(42))'); |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=item return_yes() |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
Return true (C<&PL_sv_yes>). |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=item return_no() |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
Return false (C<&PL_sv_no>). |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=item return_undef() |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
Return undef. |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
=item return_self() |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
Return the invocant (C<$self>). |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
$b->return_self; |
|
504
|
|
|
|
|
|
|
# For method chaining |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=back |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=head2 Self/Object Methods |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=over 4 |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=item get_self() |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
Get the invocant as SV*. |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
$b->get_self; |
|
517
|
|
|
|
|
|
|
# Generates: SV* self = ST(0); |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=item get_self_hv() |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
Get the invocant and its underlying hash. |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
$b->get_self_hv; |
|
524
|
|
|
|
|
|
|
# Generates: SV* self = ST(0); HV* hv = (HV*)SvRV(self); |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=item mortal($result_var, $sv_expr) |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Create a mortal copy of an SV. |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
$b->mortal('result', 'newSViv(42)'); |
|
531
|
|
|
|
|
|
|
# Generates: SV* result = sv_2mortal(newSViv(42)); |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=back |
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=head2 Hash Operations |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=over 4 |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=item hv_fetch($hv, $key, $len, $result_var) |
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
Fetch from a hash with a literal string key. |
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
$b->hv_fetch('hv', 'name', 4, 'val'); |
|
544
|
|
|
|
|
|
|
# Generates: SV** val = hv_fetch(hv, "name", 4, 0); |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=item hv_fetch_sv($hv, $key_expr, $len_expr, $result_var) |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Fetch from a hash with a C expression key. |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
$b->hv_fetch_sv('hv', 'key', 'key_len', 'val'); |
|
551
|
|
|
|
|
|
|
# Generates: SV** val = hv_fetch(hv, key, key_len, 0); |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=item hv_store($hv, $key, $len, $value) |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
Store into a hash with a literal string key. |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
$b->hv_store('hv', 'name', 4, 'newSVpv("Alice", 0)'); |
|
558
|
|
|
|
|
|
|
# Generates: (void)hv_store(hv, "name", 4, newSVpv("Alice", 0), 0); |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=item hv_store_sv($hv, $key_expr, $len_expr, $value) |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
Store into a hash with a C expression key. |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
$b->hv_store_sv('hv', 'key', 'key_len', 'newSVsv(val)'); |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=item hv_store_weak($hv, $key, $key_len, $value_expr) |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
Store a value into a hash, weakening it if it's a reference. |
|
569
|
|
|
|
|
|
|
Useful for parent/owner pointers that should not hold strong references. |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
$b->hv_store_weak('hv', 'parent', 6, 'newSVsv(ST(1))'); |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=item hv_fetch_return($hv, $key, $len) |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
Fetch from hash and return the value (or undef if not found). |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
$b->hv_fetch_return('hv', 'name', 4); |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=item hv_delete($hv, $key, $len) |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
Delete a key from a hash. |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
$b->hv_delete('hv', 'name', 4); |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=item hv_exists($hv, $key, $len, $result_var) |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
Check if a key exists in a hash. |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
$b->hv_exists('hv', 'name', 4, 'found'); |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=back |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
=head2 Array Operations |
|
594
|
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
=over 4 |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
=item av_fetch($av, $index, $result_var) |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
Fetch from an array. |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
$b->av_fetch('av', 'i', 'elem'); |
|
602
|
|
|
|
|
|
|
# Generates: SV** elem = av_fetch(av, i, 0); |
|
603
|
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
=item av_store($av, $index, $value) |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
Store into an array. |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
$b->av_store('av', 'i', 'newSViv(42)'); |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=item av_push($av, $value) |
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
Push onto an array. |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
$b->av_push('results', 'newSViv(n)'); |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=item av_len($av, $result_var) |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
Get the highest index of an array. |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
$b->av_len('av', 'last_idx'); |
|
621
|
|
|
|
|
|
|
# Generates: SSize_t last_idx = av_len(av); |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=back |
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=head2 Callbacks & Triggers |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
Methods for calling Perl code from generated XS functions. |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=over 4 |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=item call_sv($cv_expr, \@args) |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
Generate code to call a coderef with given arguments. Uses G_DISCARD so |
|
634
|
|
|
|
|
|
|
return values are discarded. Useful for calling callbacks, event handlers, |
|
635
|
|
|
|
|
|
|
or triggers. |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
$b->call_sv('cb', ['self', 'new_val']); |
|
638
|
|
|
|
|
|
|
# Generates: dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(self); ... |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=item call_method($method_name, $invocant, \@args) |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
Generate code to call a method by name on an invocant. Uses G_DISCARD |
|
643
|
|
|
|
|
|
|
so return values are discarded. Useful for calling hooks like BUILD, |
|
644
|
|
|
|
|
|
|
DEMOLISH, triggers, or callbacks. |
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
$b->call_method('_on_change', 'self', ['old_val', 'new_val']); |
|
647
|
|
|
|
|
|
|
# Generates: call_method("_on_change", G_DISCARD); |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=item rw_accessor_trigger($func_name, $attr, $len, $trigger_method) |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
Generate a read/write accessor that calls a trigger method after setting. |
|
652
|
|
|
|
|
|
|
The trigger method is called with the object and new value as arguments. |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
$b->rw_accessor_trigger('MyClass::name', 'name', 4, '_on_name_change'); |
|
655
|
|
|
|
|
|
|
# Accessor calls $self->_on_name_change($new_val) after setting |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=item accessor_lazy_builder($func_name, $attr, $len, $builder_method) |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Generate a read/write accessor with lazy initialization. On first read, |
|
660
|
|
|
|
|
|
|
if the value is undefined, calls the builder method to compute and cache |
|
661
|
|
|
|
|
|
|
the value. |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
$b->accessor_lazy_builder('MyClass::computed', 'computed', 8, '_build_computed'); |
|
664
|
|
|
|
|
|
|
# First read calls $self->_build_computed() and caches result |
|
665
|
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=item destroy_with_demolish($func_name) |
|
667
|
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
Generate a DESTROY method that checks for and calls DEMOLISH if it exists. |
|
669
|
|
|
|
|
|
|
Passes C<$in_global_destruction> as the second argument to DEMOLISH. |
|
670
|
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
$b->destroy_with_demolish('MyClass::DESTROY'); |
|
672
|
|
|
|
|
|
|
# DESTROY calls $self->DEMOLISH($in_global_destruction) if defined |
|
673
|
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
=back |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=head2 Control Flow & Extended Patterns |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
Advanced control flow helpers and expression builders. |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
=over 4 |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
=item do_loop() / end_do_while($condition) |
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
Generate a do-while loop that executes at least once. |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
$b->do_loop |
|
687
|
|
|
|
|
|
|
->line('/* loop body */') |
|
688
|
|
|
|
|
|
|
->end_do_while('ptr != NULL'); |
|
689
|
|
|
|
|
|
|
# Generates: do { ... } while (ptr != NULL); |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=item if_list_context() |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
Branch on list context. Use with C and C. |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
$b->if_list_context |
|
696
|
|
|
|
|
|
|
->return_list(['key_sv', 'val_sv']) |
|
697
|
|
|
|
|
|
|
->else |
|
698
|
|
|
|
|
|
|
->line('ST(0) = val_sv;') |
|
699
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
700
|
|
|
|
|
|
|
->endif; |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=item if_scalar_context() |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
Branch on scalar context. Use with C and C. |
|
705
|
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
$b->if_scalar_context |
|
707
|
|
|
|
|
|
|
->line('ST(0) = count_sv;') |
|
708
|
|
|
|
|
|
|
->else |
|
709
|
|
|
|
|
|
|
->return_list(['@items']) |
|
710
|
|
|
|
|
|
|
->endif; |
|
711
|
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
=item extend_stack($count_expr) |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
Extend the stack to hold more return values. |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
$b->extend_stack('num_items'); |
|
717
|
|
|
|
|
|
|
# Generates: EXTEND(SP, num_items); |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=item return_list(\@values) |
|
720
|
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
Return multiple values from XS, handling stack extension and mortalization. |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
$b->return_list(['newSViv(1)', 'newSViv(2)', 'newSViv(3)']); |
|
724
|
|
|
|
|
|
|
# Generates: EXTEND(SP, 3); ST(0) = sv_2mortal(...); ... XSRETURN(3); |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=item declare_ternary($type, $name, $cond, $true_expr, $false_expr) |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Declare a variable with ternary initialization. |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
$b->declare_ternary('SV*', 'val', 'items > 1', 'ST(1)', '&PL_sv_undef'); |
|
731
|
|
|
|
|
|
|
# Generates: SV* val = (items > 1) ? ST(1) : &PL_sv_undef; |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=item assign_ternary($var, $cond, $true_expr, $false_expr) |
|
734
|
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Ternary assignment to existing variable. |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
$b->assign_ternary('result', 'found', '*svp', '&PL_sv_undef'); |
|
738
|
|
|
|
|
|
|
# Generates: result = (found) ? *svp : &PL_sv_undef; |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=item delegate_method($func_name, $attr, $len, $target_method) |
|
741
|
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
Generate a method that delegates to an attribute's method. |
|
743
|
|
|
|
|
|
|
Passes through all arguments and returns the result. |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
$b->delegate_method('get_name', 'delegate_obj', 12, 'name'); |
|
746
|
|
|
|
|
|
|
# Generates: sub get_name { $self->{delegate_obj}->name(@_) } |
|
747
|
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
=back |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
=head2 Singleton Pattern |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
Methods for implementing the singleton design pattern. |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=over 4 |
|
755
|
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
=item singleton_accessor($func_name, $class_name) |
|
757
|
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
Generate a class method that returns the singleton instance, creating it on |
|
759
|
|
|
|
|
|
|
first access. The instance is stored in a package variable C<$Class::_instance>. |
|
760
|
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
$b->singleton_accessor('instance', 'MyApp::Config'); |
|
762
|
|
|
|
|
|
|
# MyApp::Config->instance always returns same object |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=item singleton_reset($func_name, $class_name) |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
Generate a class method that clears the singleton instance. The next call to |
|
767
|
|
|
|
|
|
|
the singleton accessor will create a fresh instance. |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
$b->singleton_reset('reset_instance', 'MyApp::Config'); |
|
770
|
|
|
|
|
|
|
# MyApp::Config->reset_instance clears the singleton |
|
771
|
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
=back |
|
773
|
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=head2 Registry Pattern |
|
775
|
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
These methods generate registry pattern accessors for storing and retrieving |
|
777
|
|
|
|
|
|
|
items in a hash attribute by key. |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
=over 4 |
|
780
|
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=item registry_add($func_name, $registry_attr) |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
Generate a method to add an item to a registry hash. Creates the registry |
|
784
|
|
|
|
|
|
|
hash automatically if it doesn't exist. Returns $self for chaining. |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
$b->registry_add('register_handler', '_handlers'); |
|
787
|
|
|
|
|
|
|
# Usage: $obj->register_handler(click => sub { ... }); |
|
788
|
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
=item registry_get($func_name, $registry_attr) |
|
790
|
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
Generate a method to retrieve an item from a registry hash by key. |
|
792
|
|
|
|
|
|
|
Returns undef if the key doesn't exist. |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
$b->registry_get('get_handler', '_handlers'); |
|
795
|
|
|
|
|
|
|
# Usage: my $handler = $obj->get_handler('click'); |
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
=item registry_remove($func_name, $registry_attr) |
|
798
|
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
Generate a method to remove and return an item from a registry hash. |
|
800
|
|
|
|
|
|
|
Returns undef if the key doesn't exist. |
|
801
|
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
$b->registry_remove('unregister_handler', '_handlers'); |
|
803
|
|
|
|
|
|
|
# Usage: my $old = $obj->unregister_handler('click'); |
|
804
|
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
=item registry_all($func_name, $registry_attr) |
|
806
|
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
Generate a context-aware method to retrieve all registry items. |
|
808
|
|
|
|
|
|
|
In list context, returns key-value pairs. In scalar context, returns a |
|
809
|
|
|
|
|
|
|
shallow copy of the registry as a hashref. |
|
810
|
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
$b->registry_all('all_handlers', '_handlers'); |
|
812
|
|
|
|
|
|
|
# List context: my %handlers = $obj->all_handlers; |
|
813
|
|
|
|
|
|
|
# Scalar context: my $hashref = $obj->all_handlers; |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
=back |
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
=head2 Method Modifiers |
|
818
|
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
These methods generate Moose/Moo-style method modifiers that wrap existing |
|
820
|
|
|
|
|
|
|
methods with before, after, or around hooks. |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
=over 4 |
|
823
|
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
=item wrap_before($func_name, $orig_name, $before_cv_name) |
|
825
|
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
Generate a wrapper that calls a "before" hook before the original method. |
|
827
|
|
|
|
|
|
|
The before hook receives the same arguments as the original. Its return |
|
828
|
|
|
|
|
|
|
value is discarded. The original's return value is preserved. |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
$b->wrap_before('save_with_log', 'MyClass::_orig_save', 'MyClass::_log_before'); |
|
831
|
|
|
|
|
|
|
# Calls _log_before(@args), then _orig_save(@args) |
|
832
|
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
=item wrap_after($func_name, $orig_name, $after_cv_name) |
|
834
|
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
Generate a wrapper that calls an "after" hook after the original method. |
|
836
|
|
|
|
|
|
|
The after hook receives the same arguments as the original. Its return |
|
837
|
|
|
|
|
|
|
value is discarded. The original's return value is preserved. |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
$b->wrap_after('save_with_notify', 'MyClass::_orig_save', 'MyClass::_notify_after'); |
|
840
|
|
|
|
|
|
|
# Calls _orig_save(@args), then _notify_after(@args) |
|
841
|
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=item wrap_around($func_name, $orig_name, $around_cv_name) |
|
843
|
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
Generate a wrapper with "around" semantics. The around hook receives the |
|
845
|
|
|
|
|
|
|
original coderef as its first argument, followed by the original arguments. |
|
846
|
|
|
|
|
|
|
The around hook has full control and can modify arguments, skip the original, |
|
847
|
|
|
|
|
|
|
or modify return values. |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
$b->wrap_around('save_cached', 'MyClass::_orig_save', 'MyClass::_cache_around'); |
|
850
|
|
|
|
|
|
|
# _cache_around receives ($orig, @args) |
|
851
|
|
|
|
|
|
|
# Can call: $orig->(@args) or skip or modify |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
=back |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
=head2 Role/Mixin Composer |
|
856
|
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
Generate multiple related methods that compose behavioral patterns. |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
=over 4 |
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
=item role($role_name, \%opts) |
|
862
|
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
Generate all methods for a single role. Available roles: |
|
864
|
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
B - Comparison methods (compare by a key attribute): |
|
866
|
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
$b->role('Comparable'); # Uses 'id' as compare key |
|
868
|
|
|
|
|
|
|
$b->role('Comparable', { compare_key => 'name' }); # Uses 'name' |
|
869
|
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
Generates: C, C, C, C, |
|
871
|
|
|
|
|
|
|
C, C. |
|
872
|
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
B - Object cloning: |
|
874
|
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
$b->role('Cloneable'); |
|
876
|
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
Generates: C - shallow clone of hash-based object. |
|
878
|
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
B - Serialization methods: |
|
880
|
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
$b->role('Serializable'); |
|
882
|
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
Generates: C, C - return hashref copy (JSON::XS compatible). |
|
884
|
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
B - Observer pattern: |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
$b->role('Observable'); |
|
888
|
|
|
|
|
|
|
$b->role('Observable', { observers_attr => '_watchers' }); |
|
889
|
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
Generates: C, C, |
|
891
|
|
|
|
|
|
|
C. |
|
892
|
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
=item with_roles(\@roles, \%opts) |
|
894
|
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
Compose multiple roles in a single call: |
|
896
|
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
$b->with_roles(['Comparable', 'Cloneable', 'Serializable']); |
|
898
|
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
# With options: |
|
900
|
|
|
|
|
|
|
$b->with_roles(['Comparable', 'Observable'], { |
|
901
|
|
|
|
|
|
|
compare_key => 'name', |
|
902
|
|
|
|
|
|
|
observers_attr => '_listeners', |
|
903
|
|
|
|
|
|
|
}); |
|
904
|
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
This is a convenience method that calls C for each role in the list. |
|
906
|
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
=back |
|
908
|
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
=head2 Prebuilt Patterns |
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
These methods generate complete XS functions for common patterns. |
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
=head3 Constructors |
|
914
|
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=over 4 |
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=item new_simple($func_name) |
|
918
|
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
Generate a minimal constructor: C. |
|
920
|
|
|
|
|
|
|
Fastest possible constructor with no argument processing. |
|
921
|
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
$b->new_simple('new'); |
|
923
|
|
|
|
|
|
|
# Generates: my $obj = Class->new; |
|
924
|
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
=item new_hash($func_name) |
|
926
|
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
Generate a flexible constructor that accepts either flat hash or hashref args. |
|
928
|
|
|
|
|
|
|
All provided args are copied into the object. |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
$b->new_hash('new'); |
|
931
|
|
|
|
|
|
|
# Supports both: |
|
932
|
|
|
|
|
|
|
# my $obj = Class->new(name => 'Alice', age => 30); |
|
933
|
|
|
|
|
|
|
# my $obj = Class->new({ name => 'Alice', age => 30 }); |
|
934
|
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
=item new_array($func_name, $num_slots) |
|
936
|
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
Generate an array-based constructor (Meow-style). Creates a blessed arrayref |
|
938
|
|
|
|
|
|
|
with pre-allocated slots initialized to undef. |
|
939
|
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
$b->new_array('new', 5); |
|
941
|
|
|
|
|
|
|
# my $obj = Class->new; # $obj->[0..4] are undef |
|
942
|
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
=item new_with_required($func_name, \@required_attrs) |
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
Generate a constructor that validates required attributes. Croaks if any |
|
946
|
|
|
|
|
|
|
required attribute is missing or undef. |
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
$b->new_with_required('new', ['name', 'id']); |
|
949
|
|
|
|
|
|
|
# Class->new(name => 'Alice'); # croaks: "Missing required attribute 'id'" |
|
950
|
|
|
|
|
|
|
# Class->new(name => 'Alice', id => 123); # OK |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
Accepts either flat hash or hashref arguments, like C. |
|
953
|
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
=item new_with_build($func_name) |
|
955
|
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
Generate a constructor that calls BUILD if it exists. Moose/Moo compatible. |
|
957
|
|
|
|
|
|
|
Accepts flat hash or hashref, and passes the args hash to BUILD. |
|
958
|
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
$b->new_with_build('new'); |
|
960
|
|
|
|
|
|
|
# Supports: |
|
961
|
|
|
|
|
|
|
# my $obj = Class->new(name => 'Alice'); |
|
962
|
|
|
|
|
|
|
# Calls: $obj->BUILD(\%args) if BUILD is defined |
|
963
|
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
=item new_complete($func_name, \@attr_specs, $call_build) |
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
Generate a unified constructor with full Moose/Moo-like attribute handling. |
|
967
|
|
|
|
|
|
|
Supports required, defaults, basic types, weak refs, coercion, and BUILD in one call. |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
use XS::JIT::Builder qw(:types); |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
$b->new_complete('new', [ |
|
972
|
|
|
|
|
|
|
{ |
|
973
|
|
|
|
|
|
|
name => 'id', |
|
974
|
|
|
|
|
|
|
required => 1, |
|
975
|
|
|
|
|
|
|
type => TYPE_INT, |
|
976
|
|
|
|
|
|
|
type_msg => 'id must be an integer', |
|
977
|
|
|
|
|
|
|
}, |
|
978
|
|
|
|
|
|
|
{ |
|
979
|
|
|
|
|
|
|
name => 'name', |
|
980
|
|
|
|
|
|
|
default_pv => 'anonymous', |
|
981
|
|
|
|
|
|
|
}, |
|
982
|
|
|
|
|
|
|
{ |
|
983
|
|
|
|
|
|
|
name => 'items', |
|
984
|
|
|
|
|
|
|
default_av => 1, # empty [] |
|
985
|
|
|
|
|
|
|
type => TYPE_ARRAYREF, |
|
986
|
|
|
|
|
|
|
}, |
|
987
|
|
|
|
|
|
|
{ |
|
988
|
|
|
|
|
|
|
name => 'meta', |
|
989
|
|
|
|
|
|
|
default_hv => 1, # empty {} |
|
990
|
|
|
|
|
|
|
}, |
|
991
|
|
|
|
|
|
|
{ |
|
992
|
|
|
|
|
|
|
name => 'count', |
|
993
|
|
|
|
|
|
|
default_iv => 0, |
|
994
|
|
|
|
|
|
|
}, |
|
995
|
|
|
|
|
|
|
{ |
|
996
|
|
|
|
|
|
|
name => 'parent', |
|
997
|
|
|
|
|
|
|
weak => 1, # weaken stored reference |
|
998
|
|
|
|
|
|
|
}, |
|
999
|
|
|
|
|
|
|
{ |
|
1000
|
|
|
|
|
|
|
name => 'age', |
|
1001
|
|
|
|
|
|
|
coerce => 'to_int', # call Class->to_int($val) |
|
1002
|
|
|
|
|
|
|
}, |
|
1003
|
|
|
|
|
|
|
], 1); # 1 = call BUILD if exists |
|
1004
|
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
Attribute spec options: |
|
1006
|
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
name - (required) Attribute name |
|
1008
|
|
|
|
|
|
|
required - Croak if missing or undef |
|
1009
|
|
|
|
|
|
|
type - TYPE_* constant for validation |
|
1010
|
|
|
|
|
|
|
type_msg - Error message for type failure |
|
1011
|
|
|
|
|
|
|
weak - Weaken stored reference (prevents circular refs) |
|
1012
|
|
|
|
|
|
|
coerce - Method name to call for coercion |
|
1013
|
|
|
|
|
|
|
default_iv - Default integer value |
|
1014
|
|
|
|
|
|
|
default_nv - Default numeric value |
|
1015
|
|
|
|
|
|
|
default_pv - Default string value |
|
1016
|
|
|
|
|
|
|
default_av - If true, default to empty [] |
|
1017
|
|
|
|
|
|
|
default_hv - If true, default to empty {} |
|
1018
|
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
Processing order: coercion → required check → type validation → |
|
1020
|
|
|
|
|
|
|
defaults → weak refs → BUILD. |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
=item constructor($func_name, \@attrs) |
|
1023
|
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
Generate a constructor with specific attributes. Attrs is arrayref of |
|
1025
|
|
|
|
|
|
|
C<[$name, $len]> pairs or simple attribute name strings. |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
$b->constructor('new', [ |
|
1028
|
|
|
|
|
|
|
['name', 4], |
|
1029
|
|
|
|
|
|
|
['age', 3], |
|
1030
|
|
|
|
|
|
|
]); |
|
1031
|
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
# Or with auto-calculated lengths: |
|
1033
|
|
|
|
|
|
|
$b->constructor('new', ['name', 'age']); |
|
1034
|
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
=back |
|
1036
|
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
=head3 Accessors |
|
1038
|
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
=over 4 |
|
1040
|
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
=item accessor($attr_name, \%options) |
|
1042
|
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
Generate a read-write accessor (or read-only with C 1>). |
|
1044
|
|
|
|
|
|
|
The function name is the attribute name. |
|
1045
|
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
$b->accessor('name'); # read-write |
|
1047
|
|
|
|
|
|
|
$b->accessor('id', { readonly => 1 }); # read-only |
|
1048
|
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
=item ro_accessor($func_name, $attr_name, $attr_len) |
|
1050
|
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
Generate a complete read-only accessor function. |
|
1052
|
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
$b->ro_accessor('get_name', 'name', 4); |
|
1054
|
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
=item rw_accessor($func_name, $attr_name, $attr_len) |
|
1056
|
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
Generate a complete read-write accessor function. |
|
1058
|
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
$b->rw_accessor('name', 'name', 4); |
|
1060
|
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
=item rw_accessor_typed($func_name, $attr_name, $attr_len, $type, $error_msg) |
|
1062
|
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
Generate a read-write accessor with inline type validation. On set, validates |
|
1064
|
|
|
|
|
|
|
the value against the specified type and croaks with C<$error_msg> if invalid. |
|
1065
|
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
use XS::JIT::Builder qw(:types); |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
$b->rw_accessor_typed('age', 'age', 3, TYPE_INT, 'age must be an integer'); |
|
1069
|
|
|
|
|
|
|
$b->rw_accessor_typed('items', 'items', 5, TYPE_ARRAYREF, 'items must be an arrayref'); |
|
1070
|
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
Type constants (from C<:types> export tag): |
|
1072
|
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
TYPE_ANY - No validation |
|
1074
|
|
|
|
|
|
|
TYPE_DEFINED - Must be defined (not undef) |
|
1075
|
|
|
|
|
|
|
TYPE_INT - Must be an integer |
|
1076
|
|
|
|
|
|
|
TYPE_NUM - Must be a number |
|
1077
|
|
|
|
|
|
|
TYPE_STR - Must be a string (not a reference) |
|
1078
|
|
|
|
|
|
|
TYPE_REF - Must be a reference |
|
1079
|
|
|
|
|
|
|
TYPE_ARRAYREF - Must be an arrayref |
|
1080
|
|
|
|
|
|
|
TYPE_HASHREF - Must be a hashref |
|
1081
|
|
|
|
|
|
|
TYPE_CODEREF - Must be a coderef |
|
1082
|
|
|
|
|
|
|
TYPE_OBJECT - Must be a blessed object |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
Note: C values bypass type checking (except for C). |
|
1085
|
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
=item rw_accessor_weak($func_name, $attr_name, $attr_len) |
|
1087
|
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
Generate a read-write accessor that auto-weakens stored references. |
|
1089
|
|
|
|
|
|
|
Use this for parent/owner references to prevent circular reference leaks. |
|
1090
|
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
$b->rw_accessor_weak('parent', 'parent', 6); |
|
1092
|
|
|
|
|
|
|
# $child->parent($parent); # stored as weak reference |
|
1093
|
|
|
|
|
|
|
# Prevents: $parent->{children} = [$child]; $child->{parent} = $parent; |
|
1094
|
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
The reference is weakened only if it's actually a reference. Scalars and |
|
1096
|
|
|
|
|
|
|
undef are stored normally. |
|
1097
|
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
=item predicate($attr_name) |
|
1099
|
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
Generate a predicate method (C). |
|
1101
|
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
$b->predicate('name'); |
|
1103
|
|
|
|
|
|
|
# Generates has_name() that returns true if 'name' key exists |
|
1104
|
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
=item clearer($attr_name) |
|
1106
|
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
Generate a clearer method (C). |
|
1108
|
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
$b->clearer('cache'); |
|
1110
|
|
|
|
|
|
|
# Generates clear_cache() that deletes 'cache' key |
|
1111
|
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=back |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
=head3 Cloning |
|
1115
|
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
=over 4 |
|
1117
|
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
=item clone_hash($func_name) |
|
1119
|
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
Generate a shallow clone method for hash-based objects. Creates a new object |
|
1121
|
|
|
|
|
|
|
with copies of all key/value pairs, blessed into the same class. |
|
1122
|
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
$b->clone_hash('clone'); |
|
1124
|
|
|
|
|
|
|
# my $copy = $obj->clone; |
|
1125
|
|
|
|
|
|
|
# $copy is independent - modifying it doesn't affect $obj |
|
1126
|
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
Note: This is a shallow clone. Nested references point to the same data. |
|
1128
|
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
=item clone_array($func_name) |
|
1130
|
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
Generate a shallow clone method for array-based objects. Creates a new object |
|
1132
|
|
|
|
|
|
|
with copies of all elements, blessed into the same class. |
|
1133
|
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
$b->clone_array('clone'); |
|
1135
|
|
|
|
|
|
|
# my $copy = $obj->clone; # blessed arrayref copy |
|
1136
|
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
Note: This is a shallow clone. Nested references point to the same data. |
|
1138
|
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
=back |
|
1140
|
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
=head1 COMPLETE EXAMPLES |
|
1142
|
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
=head2 Simple Class with Accessors |
|
1144
|
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
use XS::JIT; |
|
1146
|
|
|
|
|
|
|
use XS::JIT::Builder; |
|
1147
|
|
|
|
|
|
|
use File::Temp qw(tempdir); |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
my $cache = tempdir(CLEANUP => 1); |
|
1150
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
1151
|
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
# Constructor |
|
1153
|
|
|
|
|
|
|
$b->xs_function('person_new') |
|
1154
|
|
|
|
|
|
|
->xs_preamble |
|
1155
|
|
|
|
|
|
|
->new_hv('hv') |
|
1156
|
|
|
|
|
|
|
->if('items >= 2') |
|
1157
|
|
|
|
|
|
|
->hv_store('hv', 'name', 4, 'newSVsv(ST(1))') |
|
1158
|
|
|
|
|
|
|
->endif |
|
1159
|
|
|
|
|
|
|
->if('items >= 3') |
|
1160
|
|
|
|
|
|
|
->hv_store('hv', 'age', 3, 'newSVsv(ST(2))') |
|
1161
|
|
|
|
|
|
|
->endif |
|
1162
|
|
|
|
|
|
|
->raw('SV* self = newRV_noinc((SV*)hv);') |
|
1163
|
|
|
|
|
|
|
->raw('sv_bless(self, gv_stashpv("Person", GV_ADD));') |
|
1164
|
|
|
|
|
|
|
->return_sv('self') |
|
1165
|
|
|
|
|
|
|
->xs_end |
|
1166
|
|
|
|
|
|
|
->blank; |
|
1167
|
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
# Name accessor (read-write) |
|
1169
|
|
|
|
|
|
|
$b->rw_accessor('person_name', 'name', 4)->blank; |
|
1170
|
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
# Age accessor (read-only) |
|
1172
|
|
|
|
|
|
|
$b->ro_accessor('person_age', 'age', 3); |
|
1173
|
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
XS::JIT->compile( |
|
1175
|
|
|
|
|
|
|
code => $b->code, |
|
1176
|
|
|
|
|
|
|
name => 'Person', |
|
1177
|
|
|
|
|
|
|
cache_dir => $cache, |
|
1178
|
|
|
|
|
|
|
functions => { |
|
1179
|
|
|
|
|
|
|
'Person::new' => { source => 'person_new', is_xs_native => 1 }, |
|
1180
|
|
|
|
|
|
|
'Person::name' => { source => 'person_name', is_xs_native => 1 }, |
|
1181
|
|
|
|
|
|
|
'Person::age' => { source => 'person_age', is_xs_native => 1 }, |
|
1182
|
|
|
|
|
|
|
}, |
|
1183
|
|
|
|
|
|
|
); |
|
1184
|
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
my $p = Person->new('Alice', 30); |
|
1186
|
|
|
|
|
|
|
say $p->name; # Alice |
|
1187
|
|
|
|
|
|
|
$p->name('Bob'); # set name |
|
1188
|
|
|
|
|
|
|
say $p->age; # 30 |
|
1189
|
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
=head2 Class with Inheritance |
|
1191
|
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
1193
|
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
# Base class constructor |
|
1195
|
|
|
|
|
|
|
$b->xs_function('animal_new') |
|
1196
|
|
|
|
|
|
|
->xs_preamble |
|
1197
|
|
|
|
|
|
|
->new_hv('hv') |
|
1198
|
|
|
|
|
|
|
->hv_store('hv', 'name', 4, 'items > 1 ? newSVsv(ST(1)) : newSVpv("", 0)') |
|
1199
|
|
|
|
|
|
|
->raw('SV* self = newRV_noinc((SV*)hv);') |
|
1200
|
|
|
|
|
|
|
->raw('sv_bless(self, gv_stashpv(SvPV_nolen(ST(0)), GV_ADD));') |
|
1201
|
|
|
|
|
|
|
->return_sv('self') |
|
1202
|
|
|
|
|
|
|
->xs_end |
|
1203
|
|
|
|
|
|
|
->blank; |
|
1204
|
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
# Dog constructor (overrides Animal) |
|
1206
|
|
|
|
|
|
|
$b->xs_function('dog_new') |
|
1207
|
|
|
|
|
|
|
->xs_preamble |
|
1208
|
|
|
|
|
|
|
->new_hv('hv') |
|
1209
|
|
|
|
|
|
|
->hv_store('hv', 'name', 4, 'items > 1 ? newSVsv(ST(1)) : newSVpv("", 0)') |
|
1210
|
|
|
|
|
|
|
->hv_store('hv', 'breed', 5, 'items > 2 ? newSVsv(ST(2)) : newSVpv("mutt", 0)') |
|
1211
|
|
|
|
|
|
|
->raw('SV* self = newRV_noinc((SV*)hv);') |
|
1212
|
|
|
|
|
|
|
->raw('sv_bless(self, gv_stashpv("Dog", GV_ADD));') |
|
1213
|
|
|
|
|
|
|
->return_sv('self') |
|
1214
|
|
|
|
|
|
|
->xs_end; |
|
1215
|
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
# Compile and set up inheritance |
|
1217
|
|
|
|
|
|
|
XS::JIT->compile(...); |
|
1218
|
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
package Dog; |
|
1220
|
|
|
|
|
|
|
use parent -norequire => 'Animal'; |
|
1221
|
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
=head2 Ultra-fast Array-based Objects with Inline Ops |
|
1223
|
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
For maximum performance, use array-based objects with inline ops. |
|
1225
|
|
|
|
|
|
|
This bypasses XS call overhead entirely at compile time: |
|
1226
|
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
use XS::JIT; |
|
1228
|
|
|
|
|
|
|
use XS::JIT::Builder qw(:inline); |
|
1229
|
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
# Generate op-based accessors |
|
1231
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
1232
|
|
|
|
|
|
|
$b->op_ro_accessor('get_name', 0); # slot 0 is read-only |
|
1233
|
|
|
|
|
|
|
$b->op_rw_accessor('age', 1); # slot 1 is read-write |
|
1234
|
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
XS::JIT->compile( |
|
1236
|
|
|
|
|
|
|
code => $b->code, |
|
1237
|
|
|
|
|
|
|
name => 'Cat', |
|
1238
|
|
|
|
|
|
|
cache_dir => $cache, |
|
1239
|
|
|
|
|
|
|
functions => { |
|
1240
|
|
|
|
|
|
|
'Cat::name' => { source => 'get_name', is_xs_native => 1 }, |
|
1241
|
|
|
|
|
|
|
'Cat::age' => { source => 'age', is_xs_native => 1 }, |
|
1242
|
|
|
|
|
|
|
}, |
|
1243
|
|
|
|
|
|
|
); |
|
1244
|
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
# Register inline ops for compile-time optimization |
|
1246
|
|
|
|
|
|
|
XS::JIT::Builder::inline_init(); |
|
1247
|
|
|
|
|
|
|
XS::JIT::Builder::inline_register(\&Cat::name, INLINE_GETTER, 0); |
|
1248
|
|
|
|
|
|
|
XS::JIT::Builder::inline_register(\&Cat::age, INLINE_SETTER, 1); |
|
1249
|
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
# Now function calls are replaced with custom ops at compile time! |
|
1251
|
|
|
|
|
|
|
package Cat; |
|
1252
|
|
|
|
|
|
|
sub new { bless [$_[1], $_[2]], $_[0] } |
|
1253
|
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
package main; |
|
1255
|
|
|
|
|
|
|
my $cat = Cat->new('Whiskers', 3); |
|
1256
|
|
|
|
|
|
|
say $cat->name; # Inline op - no XS call overhead |
|
1257
|
|
|
|
|
|
|
$cat->age(4); # Inline setter |
|
1258
|
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
=head2 Inline Op Types |
|
1260
|
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
The following constants are available via C<:inline> export tag: |
|
1262
|
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
=over 4 |
|
1264
|
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
=item INLINE_NONE (0) |
|
1266
|
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
No inlining. |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
=item INLINE_GETTER (1) |
|
1270
|
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
Read-only slot accessor. Replaces C<< $obj->name >> with a custom op |
|
1272
|
|
|
|
|
|
|
that reads directly from C<< $obj->[slot] >>. |
|
1273
|
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
=item INLINE_SETTER (2) |
|
1275
|
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
Read-write slot accessor. Supports both getter and setter modes. |
|
1277
|
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
=item INLINE_HV_GETTER (3) |
|
1279
|
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
Read-only hash accessor (not yet implemented). |
|
1281
|
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
=item INLINE_HV_SETTER (4) |
|
1283
|
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
Read-write hash accessor (not yet implemented). |
|
1285
|
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
=back |
|
1287
|
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
=head2 Custom Op Builder Methods |
|
1289
|
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
These methods generate code for building custom Perl ops, enabling |
|
1291
|
|
|
|
|
|
|
compile-time optimization through call checkers. |
|
1292
|
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
=head3 PP Function Builders |
|
1294
|
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
PP (push-pop) functions are the runtime handlers for custom ops. |
|
1296
|
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
=over 4 |
|
1298
|
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
=item pp_start($name) |
|
1300
|
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
Start a pp function definition. |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
$b->pp_start('pp_my_op'); |
|
1304
|
|
|
|
|
|
|
# Generates: OP* pp_my_op(pTHX) { |
|
1305
|
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
=item pp_end() |
|
1307
|
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
End a pp function with C. |
|
1309
|
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
$b->pp_end; |
|
1311
|
|
|
|
|
|
|
# Generates: return NORMAL; } |
|
1312
|
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
=item pp_dsp() |
|
1314
|
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
Add dSP declaration for stack access. |
|
1316
|
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
$b->pp_dsp; |
|
1318
|
|
|
|
|
|
|
# Generates: dSP; |
|
1319
|
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
=item pp_get_self() |
|
1321
|
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
Get the invocant from the stack without popping. |
|
1323
|
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
$b->pp_get_self; |
|
1325
|
|
|
|
|
|
|
# Generates: SV* self = TOPs; |
|
1326
|
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
=item pp_pop_self() |
|
1328
|
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
Pop the invocant from the stack. |
|
1330
|
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
$b->pp_pop_self; |
|
1332
|
|
|
|
|
|
|
# Generates: SV* self = POPs; |
|
1333
|
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
=item pp_pop_sv($name) |
|
1335
|
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
Pop an SV from the stack. |
|
1337
|
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
$b->pp_pop_sv('value'); |
|
1339
|
|
|
|
|
|
|
# Generates: SV* value = POPs; |
|
1340
|
|
|
|
|
|
|
|
|
1341
|
|
|
|
|
|
|
=item pp_pop_nv($name) |
|
1342
|
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
Pop a numeric value from the stack. |
|
1344
|
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
$b->pp_pop_nv('amount'); |
|
1346
|
|
|
|
|
|
|
# Generates: NV amount = POPn; |
|
1347
|
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
=item pp_pop_iv($name) |
|
1349
|
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
Pop an integer value from the stack. |
|
1351
|
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
$b->pp_pop_iv('count'); |
|
1353
|
|
|
|
|
|
|
# Generates: IV count = POPi; |
|
1354
|
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
=item pp_get_slots() |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
Get the slots array from self (for array-based objects). |
|
1358
|
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
$b->pp_get_slots; |
|
1360
|
|
|
|
|
|
|
# Generates: AV* slots = (AV*)SvRV(self); |
|
1361
|
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
=item pp_slot($name, $index) |
|
1363
|
|
|
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
Access a specific slot from the slots array. |
|
1365
|
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
$b->pp_slot('name_sv', 0); |
|
1367
|
|
|
|
|
|
|
# Generates: SV* name_sv = *av_fetch(slots, 0, 0); |
|
1368
|
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
=item pp_return_sv($expr) |
|
1370
|
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
Return an SV to the stack. |
|
1372
|
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
$b->pp_return_sv('name_sv'); |
|
1374
|
|
|
|
|
|
|
# Generates: SETs(name_sv); RETURN; |
|
1375
|
|
|
|
|
|
|
|
|
1376
|
|
|
|
|
|
|
=item pp_return_nv($expr) |
|
1377
|
|
|
|
|
|
|
|
|
1378
|
|
|
|
|
|
|
Return a numeric value. |
|
1379
|
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
$b->pp_return_nv('result'); |
|
1381
|
|
|
|
|
|
|
# Generates: SETn(result); RETURN; |
|
1382
|
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
=item pp_return_iv($expr) |
|
1384
|
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
Return an integer value. |
|
1386
|
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
$b->pp_return_iv('count'); |
|
1388
|
|
|
|
|
|
|
# Generates: SETi(count); RETURN; |
|
1389
|
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
=item pp_return_pv($expr) |
|
1391
|
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
Return a string value. |
|
1393
|
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
$b->pp_return_pv('str'); |
|
1395
|
|
|
|
|
|
|
# Generates: SETs(sv_2mortal(newSVpv(str, 0))); RETURN; |
|
1396
|
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
=item pp_return() |
|
1398
|
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
Return without modifying the stack. |
|
1400
|
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
$b->pp_return; |
|
1402
|
|
|
|
|
|
|
# Generates: return NORMAL; |
|
1403
|
|
|
|
|
|
|
|
|
1404
|
|
|
|
|
|
|
=back |
|
1405
|
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
=head3 Call Checker Builders |
|
1407
|
|
|
|
|
|
|
|
|
1408
|
|
|
|
|
|
|
Call checkers run at compile time to replace subroutine calls with custom ops. |
|
1409
|
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
=over 4 |
|
1411
|
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
=item ck_start($name) |
|
1413
|
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
Start a call checker function. |
|
1415
|
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
$b->ck_start('ck_my_method'); |
|
1417
|
|
|
|
|
|
|
# Generates: OP* ck_my_method(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
1418
|
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
=item ck_end() |
|
1420
|
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
End a call checker with fallback. |
|
1422
|
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
$b->ck_end; |
|
1424
|
|
|
|
|
|
|
# Generates: return ck_entersub_args_proto_or_list(...); } |
|
1425
|
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
=item ck_preamble() |
|
1427
|
|
|
|
|
|
|
|
|
1428
|
|
|
|
|
|
|
Add standard call checker preamble to extract arguments. |
|
1429
|
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
$b->ck_preamble; |
|
1431
|
|
|
|
|
|
|
# Extracts pushmark, args, method from the optree |
|
1432
|
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
=item ck_build_unop($pp_func, $targ_expr) |
|
1434
|
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
Build a custom unary op (one argument). |
|
1436
|
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
$b->ck_build_unop('pp_my_getter', 'slot_num'); |
|
1438
|
|
|
|
|
|
|
# Generates code to create OP_CUSTOM with the specified pp function |
|
1439
|
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
=item ck_build_binop($pp_func, $targ_expr) |
|
1441
|
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
Build a custom binary op (two arguments). |
|
1443
|
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
$b->ck_build_binop('pp_my_setter', 'slot_num'); |
|
1445
|
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
=item ck_fallback() |
|
1447
|
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
Fall through to default call checking. |
|
1449
|
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
$b->ck_fallback; |
|
1451
|
|
|
|
|
|
|
# Generates: return ck_entersub_args_proto_or_list(...); |
|
1452
|
|
|
|
|
|
|
|
|
1453
|
|
|
|
|
|
|
=back |
|
1454
|
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
=head3 XOP Helpers |
|
1456
|
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
XOP (extended op) declarations register custom ops with Perl. |
|
1458
|
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
=over 4 |
|
1460
|
|
|
|
|
|
|
|
|
1461
|
|
|
|
|
|
|
=item xop_declare($name, $pp_func, $desc) |
|
1462
|
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
Declare a custom op descriptor. |
|
1464
|
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
$b->xop_declare('my_xop', 'pp_my_op', 'my custom op'); |
|
1466
|
|
|
|
|
|
|
# Generates static XOP declaration and registration |
|
1467
|
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
=item register_checker($cv_expr, $ck_func, $ckobj_expr) |
|
1469
|
|
|
|
|
|
|
|
|
1470
|
|
|
|
|
|
|
Register a call checker for a CV. |
|
1471
|
|
|
|
|
|
|
|
|
1472
|
|
|
|
|
|
|
$b->register_checker('cv', 'ck_my_method', 'newSViv(slot)'); |
|
1473
|
|
|
|
|
|
|
# Generates: cv_set_call_checker(cv, ck_my_method, ckobj); |
|
1474
|
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
=back |
|
1476
|
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
=head3 Custom Op Example |
|
1478
|
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
1480
|
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
# Declare the XOP |
|
1482
|
|
|
|
|
|
|
$b->xop_declare('slot_getter_xop', 'pp_slot_getter', 'slot getter'); |
|
1483
|
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
# Build the pp function |
|
1485
|
|
|
|
|
|
|
$b->pp_start('pp_slot_getter') |
|
1486
|
|
|
|
|
|
|
->pp_dsp |
|
1487
|
|
|
|
|
|
|
->pp_get_self |
|
1488
|
|
|
|
|
|
|
->pp_get_slots |
|
1489
|
|
|
|
|
|
|
->line('IV slot = PL_op->op_targ;') |
|
1490
|
|
|
|
|
|
|
->pp_slot('val', 'slot') |
|
1491
|
|
|
|
|
|
|
->pp_return_sv('val') |
|
1492
|
|
|
|
|
|
|
->pp_end; |
|
1493
|
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
# Build the call checker |
|
1495
|
|
|
|
|
|
|
$b->ck_start('ck_slot_getter') |
|
1496
|
|
|
|
|
|
|
->ck_preamble |
|
1497
|
|
|
|
|
|
|
->ck_build_unop('pp_slot_getter', 'SvIV(ckobj)') |
|
1498
|
|
|
|
|
|
|
->ck_end; |
|
1499
|
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
=head2 Direct AvARRAY Access |
|
1501
|
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
These methods generate code for ultra-fast array slot access, bypassing av_fetch/av_store. |
|
1503
|
|
|
|
|
|
|
Inspired by Meow's optimized accessors. |
|
1504
|
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
=over 4 |
|
1506
|
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
=item av_direct($result_var, $av_expr) |
|
1508
|
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
Get direct array pointer for fast slot access. |
|
1510
|
|
|
|
|
|
|
|
|
1511
|
|
|
|
|
|
|
$b->av_direct('slots', '(AV*)SvRV(self)'); |
|
1512
|
|
|
|
|
|
|
# Generates: SV** slots = AvARRAY((AV*)SvRV(self)); |
|
1513
|
|
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
=item av_slot_read($result_var, $slots_var, $slot) |
|
1515
|
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
Read a slot value with undef fallback. |
|
1517
|
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
$b->av_slot_read('val', 'slots', 0); |
|
1519
|
|
|
|
|
|
|
# Generates: SV* val = slots[0] ? slots[0] : &PL_sv_undef; |
|
1520
|
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
=item av_slot_write($slots_var, $slot, $value) |
|
1522
|
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
Write a value to a slot with ref counting. |
|
1524
|
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
$b->av_slot_write('slots', 0, 'new_val'); |
|
1526
|
|
|
|
|
|
|
# Generates proper SvREFCNT_dec and SvREFCNT_inc |
|
1527
|
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
=back |
|
1529
|
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
=head2 Type Checking |
|
1531
|
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
Generate type validation code. Constants can be exported via C<:types> tag. |
|
1533
|
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
use XS::JIT::Builder qw(:types); |
|
1535
|
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
$b->check_value_type('val', TYPE_ARRAYREF, undef, 'value must be an arrayref'); |
|
1537
|
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
=over 4 |
|
1539
|
|
|
|
|
|
|
|
|
1540
|
|
|
|
|
|
|
=item check_value_type($sv, $type, $classname, $error_msg) |
|
1541
|
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
Generate a type check with croak on failure. |
|
1543
|
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
$b->check_value_type('ST(1)', TYPE_HASHREF, undef, 'Expected hashref'); |
|
1545
|
|
|
|
|
|
|
# Generates: if (!(SvROK(ST(1)) && SvTYPE(SvRV(ST(1))) == SVt_PVHV)) croak(...); |
|
1546
|
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
Type constants: |
|
1548
|
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
TYPE_ANY - No check |
|
1550
|
|
|
|
|
|
|
TYPE_DEFINED - SvOK |
|
1551
|
|
|
|
|
|
|
TYPE_INT - SvIOK |
|
1552
|
|
|
|
|
|
|
TYPE_NUM - SvNOK or SvIOK |
|
1553
|
|
|
|
|
|
|
TYPE_STR - SvPOK |
|
1554
|
|
|
|
|
|
|
TYPE_REF - SvROK |
|
1555
|
|
|
|
|
|
|
TYPE_ARRAYREF - SvROK + SVt_PVAV |
|
1556
|
|
|
|
|
|
|
TYPE_HASHREF - SvROK + SVt_PVHV |
|
1557
|
|
|
|
|
|
|
TYPE_CODEREF - SvROK + SVt_PVCV |
|
1558
|
|
|
|
|
|
|
TYPE_OBJECT - sv_isobject |
|
1559
|
|
|
|
|
|
|
TYPE_BLESSED - sv_derived_from($classname) |
|
1560
|
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
=back |
|
1562
|
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
=head2 Lazy Initialization |
|
1564
|
|
|
|
|
|
|
|
|
1565
|
|
|
|
|
|
|
Generate accessors that lazily initialize with a default value. |
|
1566
|
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
=over 4 |
|
1568
|
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
=item lazy_init_dor($func_name, $attr_name, $attr_len, $default_expr, $is_mortal) |
|
1570
|
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
Generate lazy init accessor using //= (defined-or-assign). |
|
1572
|
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
$b->lazy_init_dor('MyClass_items', 'items', 5, 'newRV_noinc((SV*)newAV())', 0); |
|
1574
|
|
|
|
|
|
|
# sub items { $self->{items} //= [] } |
|
1575
|
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
=item lazy_init_or($func_name, $attr_name, $attr_len, $default_expr, $is_mortal) |
|
1577
|
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
Generate lazy init accessor using ||= (or-assign). |
|
1579
|
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
$b->lazy_init_or('MyClass_name', 'name', 4, 'newSVpvn("default", 7)', 0); |
|
1581
|
|
|
|
|
|
|
# sub name { $self->{name} ||= 'default' } |
|
1582
|
|
|
|
|
|
|
|
|
1583
|
|
|
|
|
|
|
=item slot_lazy_init_dor($func_name, $slot, $default_expr, $is_mortal) |
|
1584
|
|
|
|
|
|
|
|
|
1585
|
|
|
|
|
|
|
Slot-based lazy init with //=. |
|
1586
|
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
$b->slot_lazy_init_dor('get_cache', 2, 'newRV_noinc((SV*)newHV())', 0); |
|
1588
|
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
=item slot_lazy_init_or($func_name, $slot, $default_expr, $is_mortal) |
|
1590
|
|
|
|
|
|
|
|
|
1591
|
|
|
|
|
|
|
Slot-based lazy init with ||=. |
|
1592
|
|
|
|
|
|
|
|
|
1593
|
|
|
|
|
|
|
=back |
|
1594
|
|
|
|
|
|
|
|
|
1595
|
|
|
|
|
|
|
=head2 Setter Patterns |
|
1596
|
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
=over 4 |
|
1598
|
|
|
|
|
|
|
|
|
1599
|
|
|
|
|
|
|
=item setter_chain($func_name, $attr_name, $attr_len) |
|
1600
|
|
|
|
|
|
|
|
|
1601
|
|
|
|
|
|
|
Generate a setter that returns $self for chaining. |
|
1602
|
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
$b->setter_chain('set_name', 'name', 4); |
|
1604
|
|
|
|
|
|
|
# $obj->set_name('foo')->set_age(42) |
|
1605
|
|
|
|
|
|
|
|
|
1606
|
|
|
|
|
|
|
=item slot_setter_chain($func_name, $slot) |
|
1607
|
|
|
|
|
|
|
|
|
1608
|
|
|
|
|
|
|
Slot-based setter chain. |
|
1609
|
|
|
|
|
|
|
|
|
1610
|
|
|
|
|
|
|
=item setter_return_value($func_name, $attr_name, $attr_len) |
|
1611
|
|
|
|
|
|
|
|
|
1612
|
|
|
|
|
|
|
Generate a setter that returns the value set. |
|
1613
|
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
$b->setter_return_value('set_name', 'name', 4); |
|
1615
|
|
|
|
|
|
|
# my $v = $obj->set_name('foo'); # $v is 'foo' |
|
1616
|
|
|
|
|
|
|
|
|
1617
|
|
|
|
|
|
|
=back |
|
1618
|
|
|
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
=head2 Array Attribute Operations |
|
1620
|
|
|
|
|
|
|
|
|
1621
|
|
|
|
|
|
|
Generate methods for manipulating array attributes in hash-based objects. |
|
1622
|
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
=over 4 |
|
1624
|
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
=item attr_push($func_name, $attr_name, $attr_len) |
|
1626
|
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
$b->attr_push('add_item', 'items', 5); |
|
1628
|
|
|
|
|
|
|
# push @{$self->{items}}, @values |
|
1629
|
|
|
|
|
|
|
|
|
1630
|
|
|
|
|
|
|
=item attr_pop($func_name, $attr_name, $attr_len) |
|
1631
|
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
$b->attr_pop('pop_item', 'items', 5); |
|
1633
|
|
|
|
|
|
|
# pop @{$self->{items}} |
|
1634
|
|
|
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
=item attr_shift($func_name, $attr_name, $attr_len) |
|
1636
|
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
$b->attr_shift('shift_item', 'items', 5); |
|
1638
|
|
|
|
|
|
|
# shift @{$self->{items}} |
|
1639
|
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
=item attr_unshift($func_name, $attr_name, $attr_len) |
|
1641
|
|
|
|
|
|
|
|
|
1642
|
|
|
|
|
|
|
$b->attr_unshift('prepend_item', 'items', 5); |
|
1643
|
|
|
|
|
|
|
# unshift @{$self->{items}}, @values |
|
1644
|
|
|
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
=item attr_count($func_name, $attr_name, $attr_len) |
|
1646
|
|
|
|
|
|
|
|
|
1647
|
|
|
|
|
|
|
$b->attr_count('item_count', 'items', 5); |
|
1648
|
|
|
|
|
|
|
# scalar @{$self->{items}} |
|
1649
|
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
=item attr_clear($func_name, $attr_name, $attr_len) |
|
1651
|
|
|
|
|
|
|
|
|
1652
|
|
|
|
|
|
|
$b->attr_clear('clear_items', 'items', 5); |
|
1653
|
|
|
|
|
|
|
# @{$self->{items}} = () |
|
1654
|
|
|
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
=back |
|
1656
|
|
|
|
|
|
|
|
|
1657
|
|
|
|
|
|
|
=head2 Hash Attribute Operations |
|
1658
|
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
Generate methods for manipulating hash attributes. |
|
1660
|
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
=over 4 |
|
1662
|
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
=item attr_keys($func_name, $attr_name, $attr_len) |
|
1664
|
|
|
|
|
|
|
|
|
1665
|
|
|
|
|
|
|
$b->attr_keys('cache_keys', 'cache', 5); |
|
1666
|
|
|
|
|
|
|
# keys %{$self->{cache}} |
|
1667
|
|
|
|
|
|
|
|
|
1668
|
|
|
|
|
|
|
=item attr_values($func_name, $attr_name, $attr_len) |
|
1669
|
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
$b->attr_values('cache_values', 'cache', 5); |
|
1671
|
|
|
|
|
|
|
# values %{$self->{cache}} |
|
1672
|
|
|
|
|
|
|
|
|
1673
|
|
|
|
|
|
|
=item attr_delete($func_name, $attr_name, $attr_len) |
|
1674
|
|
|
|
|
|
|
|
|
1675
|
|
|
|
|
|
|
$b->attr_delete('delete_cache', 'cache', 5); |
|
1676
|
|
|
|
|
|
|
# delete $self->{cache}{$key} |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
=item attr_hash_clear($func_name, $attr_name, $attr_len) |
|
1679
|
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
$b->attr_hash_clear('clear_cache', 'cache', 5); |
|
1681
|
|
|
|
|
|
|
# %{$self->{cache}} = () |
|
1682
|
|
|
|
|
|
|
|
|
1683
|
|
|
|
|
|
|
=back |
|
1684
|
|
|
|
|
|
|
|
|
1685
|
|
|
|
|
|
|
=head2 Conditional DSL (Struct::Conditional Format) |
|
1686
|
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
Generate conditional C code from declarative Perl data structures. This uses |
|
1688
|
|
|
|
|
|
|
the same format as L, allowing you to define conditionals |
|
1689
|
|
|
|
|
|
|
as data rather than imperative code. |
|
1690
|
|
|
|
|
|
|
|
|
1691
|
|
|
|
|
|
|
=over 4 |
|
1692
|
|
|
|
|
|
|
|
|
1693
|
|
|
|
|
|
|
=item conditional(\%struct) |
|
1694
|
|
|
|
|
|
|
|
|
1695
|
|
|
|
|
|
|
Generate C code from a Struct::Conditional-compatible hashref. Supports |
|
1696
|
|
|
|
|
|
|
C and C patterns. |
|
1697
|
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
B |
|
1699
|
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
$b->conditional({ |
|
1701
|
|
|
|
|
|
|
if => { |
|
1702
|
|
|
|
|
|
|
key => 'num', # C variable to test |
|
1703
|
|
|
|
|
|
|
gt => 0, # expression: greater than |
|
1704
|
|
|
|
|
|
|
then => { |
|
1705
|
|
|
|
|
|
|
line => 'XSRETURN_IV(1);' |
|
1706
|
|
|
|
|
|
|
} |
|
1707
|
|
|
|
|
|
|
}, |
|
1708
|
|
|
|
|
|
|
else => { |
|
1709
|
|
|
|
|
|
|
then => { |
|
1710
|
|
|
|
|
|
|
line => 'XSRETURN_IV(0);' |
|
1711
|
|
|
|
|
|
|
} |
|
1712
|
|
|
|
|
|
|
} |
|
1713
|
|
|
|
|
|
|
}); |
|
1714
|
|
|
|
|
|
|
|
|
1715
|
|
|
|
|
|
|
B |
|
1716
|
|
|
|
|
|
|
|
|
1717
|
|
|
|
|
|
|
$b->conditional({ |
|
1718
|
|
|
|
|
|
|
if => { |
|
1719
|
|
|
|
|
|
|
key => 'type_sv', |
|
1720
|
|
|
|
|
|
|
eq => 'int', |
|
1721
|
|
|
|
|
|
|
then => { line => 'handle_int(val);' } |
|
1722
|
|
|
|
|
|
|
}, |
|
1723
|
|
|
|
|
|
|
elsif => { |
|
1724
|
|
|
|
|
|
|
key => 'type_sv', |
|
1725
|
|
|
|
|
|
|
eq => 'str', |
|
1726
|
|
|
|
|
|
|
then => { line => 'handle_str(val);' } |
|
1727
|
|
|
|
|
|
|
}, |
|
1728
|
|
|
|
|
|
|
else => { |
|
1729
|
|
|
|
|
|
|
then => { line => 'handle_default(val);' } |
|
1730
|
|
|
|
|
|
|
} |
|
1731
|
|
|
|
|
|
|
}); |
|
1732
|
|
|
|
|
|
|
|
|
1733
|
|
|
|
|
|
|
B |
|
1734
|
|
|
|
|
|
|
|
|
1735
|
|
|
|
|
|
|
$b->conditional({ |
|
1736
|
|
|
|
|
|
|
if => { key => 'n', gt => 100, then => { return_iv => '3' } }, |
|
1737
|
|
|
|
|
|
|
elsif => [ |
|
1738
|
|
|
|
|
|
|
{ key => 'n', gt => 50, then => { return_iv => '2' } }, |
|
1739
|
|
|
|
|
|
|
{ key => 'n', gt => 0, then => { return_iv => '1' } }, |
|
1740
|
|
|
|
|
|
|
], |
|
1741
|
|
|
|
|
|
|
else => { then => { return_iv => '0' } } |
|
1742
|
|
|
|
|
|
|
}); |
|
1743
|
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
B |
|
1745
|
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
$b->conditional({ |
|
1747
|
|
|
|
|
|
|
given => { |
|
1748
|
|
|
|
|
|
|
key => 'type_sv', |
|
1749
|
|
|
|
|
|
|
when => { |
|
1750
|
|
|
|
|
|
|
int => { line => 'XSRETURN_IV(1);' }, |
|
1751
|
|
|
|
|
|
|
str => { line => 'XSRETURN_IV(2);' }, |
|
1752
|
|
|
|
|
|
|
array => { line => 'XSRETURN_IV(3);' }, |
|
1753
|
|
|
|
|
|
|
default => { line => 'XSRETURN_IV(0);' } |
|
1754
|
|
|
|
|
|
|
} |
|
1755
|
|
|
|
|
|
|
} |
|
1756
|
|
|
|
|
|
|
}); |
|
1757
|
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
B |
|
1759
|
|
|
|
|
|
|
|
|
1760
|
|
|
|
|
|
|
$b->conditional({ |
|
1761
|
|
|
|
|
|
|
given => { |
|
1762
|
|
|
|
|
|
|
key => 'country', |
|
1763
|
|
|
|
|
|
|
when => [ |
|
1764
|
|
|
|
|
|
|
{ m => 'Thai', then => { return_iv => '1' } }, |
|
1765
|
|
|
|
|
|
|
{ m => 'Indo', then => { return_iv => '2' } }, |
|
1766
|
|
|
|
|
|
|
], |
|
1767
|
|
|
|
|
|
|
default => { return_iv => '0' } |
|
1768
|
|
|
|
|
|
|
} |
|
1769
|
|
|
|
|
|
|
}); |
|
1770
|
|
|
|
|
|
|
|
|
1771
|
|
|
|
|
|
|
B |
|
1772
|
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
$b->conditional({ |
|
1774
|
|
|
|
|
|
|
if => { |
|
1775
|
|
|
|
|
|
|
key => 'x', |
|
1776
|
|
|
|
|
|
|
gt => 0, |
|
1777
|
|
|
|
|
|
|
and => { |
|
1778
|
|
|
|
|
|
|
key => 'y', |
|
1779
|
|
|
|
|
|
|
gt => 0 |
|
1780
|
|
|
|
|
|
|
}, |
|
1781
|
|
|
|
|
|
|
then => { line => 'handle_positive_quadrant();' } |
|
1782
|
|
|
|
|
|
|
} |
|
1783
|
|
|
|
|
|
|
}); |
|
1784
|
|
|
|
|
|
|
|
|
1785
|
|
|
|
|
|
|
$b->conditional({ |
|
1786
|
|
|
|
|
|
|
if => { |
|
1787
|
|
|
|
|
|
|
key => 'status', |
|
1788
|
|
|
|
|
|
|
eq => 'active', |
|
1789
|
|
|
|
|
|
|
or => { |
|
1790
|
|
|
|
|
|
|
key => 'status', |
|
1791
|
|
|
|
|
|
|
eq => 'pending' |
|
1792
|
|
|
|
|
|
|
}, |
|
1793
|
|
|
|
|
|
|
then => { line => 'process();' } |
|
1794
|
|
|
|
|
|
|
} |
|
1795
|
|
|
|
|
|
|
}); |
|
1796
|
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
B |
|
1798
|
|
|
|
|
|
|
|
|
1799
|
|
|
|
|
|
|
gt => N # SvIV(key) > N |
|
1800
|
|
|
|
|
|
|
lt => N # SvIV(key) < N |
|
1801
|
|
|
|
|
|
|
gte => N # SvIV(key) >= N |
|
1802
|
|
|
|
|
|
|
lte => N # SvIV(key) <= N |
|
1803
|
|
|
|
|
|
|
eq => 'str' # strEQ(SvPV_nolen(key), "str") |
|
1804
|
|
|
|
|
|
|
ne => 'str' # !strEQ(...) |
|
1805
|
|
|
|
|
|
|
m => 'pat' # strstr(SvPV_nolen(key), "pat") != NULL |
|
1806
|
|
|
|
|
|
|
im => 'pat' # case-insensitive match |
|
1807
|
|
|
|
|
|
|
nm => 'pat' # NOT match |
|
1808
|
|
|
|
|
|
|
inm => 'pat' # case-insensitive NOT match |
|
1809
|
|
|
|
|
|
|
exists => 1 # SvOK(key) |
|
1810
|
|
|
|
|
|
|
true => 1 # SvTRUE(key) |
|
1811
|
|
|
|
|
|
|
|
|
1812
|
|
|
|
|
|
|
B |
|
1813
|
|
|
|
|
|
|
|
|
1814
|
|
|
|
|
|
|
then => { line => '...' } # raw C line |
|
1815
|
|
|
|
|
|
|
then => { return_iv => 'N' } # XSRETURN_IV(N) |
|
1816
|
|
|
|
|
|
|
then => { return_nv => 'N' } # return double |
|
1817
|
|
|
|
|
|
|
then => { return_pv => '"str"' } # XSRETURN_PV(str) |
|
1818
|
|
|
|
|
|
|
then => { return_sv => 'expr' } # return SV expression |
|
1819
|
|
|
|
|
|
|
then => { croak => 'message' } # croak("message") |
|
1820
|
|
|
|
|
|
|
then => [ {...}, {...} ] # multiple actions |
|
1821
|
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
=back |
|
1823
|
|
|
|
|
|
|
|
|
1824
|
|
|
|
|
|
|
=head3 Complete Conditional Example |
|
1825
|
|
|
|
|
|
|
|
|
1826
|
|
|
|
|
|
|
use XS::JIT; |
|
1827
|
|
|
|
|
|
|
use XS::JIT::Builder; |
|
1828
|
|
|
|
|
|
|
|
|
1829
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
1830
|
|
|
|
|
|
|
|
|
1831
|
|
|
|
|
|
|
$b->xs_function('classify_number') |
|
1832
|
|
|
|
|
|
|
->xs_preamble |
|
1833
|
|
|
|
|
|
|
->declare_iv('num', 'SvIV(ST(0))') |
|
1834
|
|
|
|
|
|
|
->conditional({ |
|
1835
|
|
|
|
|
|
|
if => { |
|
1836
|
|
|
|
|
|
|
key => 'num', |
|
1837
|
|
|
|
|
|
|
gt => 0, |
|
1838
|
|
|
|
|
|
|
then => { return_pv => '"positive"' } |
|
1839
|
|
|
|
|
|
|
}, |
|
1840
|
|
|
|
|
|
|
elsif => { |
|
1841
|
|
|
|
|
|
|
key => 'num', |
|
1842
|
|
|
|
|
|
|
lt => 0, |
|
1843
|
|
|
|
|
|
|
then => { return_pv => '"negative"' } |
|
1844
|
|
|
|
|
|
|
}, |
|
1845
|
|
|
|
|
|
|
else => { |
|
1846
|
|
|
|
|
|
|
then => { return_pv => '"zero"' } |
|
1847
|
|
|
|
|
|
|
} |
|
1848
|
|
|
|
|
|
|
}) |
|
1849
|
|
|
|
|
|
|
->xs_end; |
|
1850
|
|
|
|
|
|
|
|
|
1851
|
|
|
|
|
|
|
XS::JIT->compile( |
|
1852
|
|
|
|
|
|
|
code => $b->code, |
|
1853
|
|
|
|
|
|
|
name => 'NumClass', |
|
1854
|
|
|
|
|
|
|
functions => { |
|
1855
|
|
|
|
|
|
|
'NumClass::classify' => { source => 'classify_number', is_xs_native => 1 } |
|
1856
|
|
|
|
|
|
|
} |
|
1857
|
|
|
|
|
|
|
); |
|
1858
|
|
|
|
|
|
|
|
|
1859
|
|
|
|
|
|
|
say NumClass::classify(42); # "positive" |
|
1860
|
|
|
|
|
|
|
say NumClass::classify(-5); # "negative" |
|
1861
|
|
|
|
|
|
|
say NumClass::classify(0); # "zero" |
|
1862
|
|
|
|
|
|
|
|
|
1863
|
|
|
|
|
|
|
=head2 Switch Statement Helper |
|
1864
|
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
The C method provides an optimized way to generate multi-branch conditionals |
|
1866
|
|
|
|
|
|
|
on a single key, avoiding Perl's hash duplicate-key limitation with C. It |
|
1867
|
|
|
|
|
|
|
automatically detects whether all cases use the same comparison type and applies |
|
1868
|
|
|
|
|
|
|
optimizations. |
|
1869
|
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
=over 4 |
|
1871
|
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
=item switch($key, \@cases, [\%default]) |
|
1873
|
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
Generate optimized switch-style conditional code. The key is a C variable name, |
|
1875
|
|
|
|
|
|
|
cases is an arrayref of clause hashrefs, and default is an optional hashref of |
|
1876
|
|
|
|
|
|
|
actions for the default case. |
|
1877
|
|
|
|
|
|
|
|
|
1878
|
|
|
|
|
|
|
B |
|
1879
|
|
|
|
|
|
|
|
|
1880
|
|
|
|
|
|
|
$b->switch('type_str', [ |
|
1881
|
|
|
|
|
|
|
{ eq => 'int', then => { return_iv => '1' } }, |
|
1882
|
|
|
|
|
|
|
{ eq => 'str', then => { return_iv => '2' } }, |
|
1883
|
|
|
|
|
|
|
{ eq => 'array', then => { return_iv => '3' } }, |
|
1884
|
|
|
|
|
|
|
], { return_iv => '0' }); |
|
1885
|
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
This generates optimized C code that: |
|
1887
|
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
=over 4 |
|
1889
|
|
|
|
|
|
|
|
|
1890
|
|
|
|
|
|
|
=item * Caches C once at the start |
|
1891
|
|
|
|
|
|
|
|
|
1892
|
|
|
|
|
|
|
=item * Uses C with length pre-check for efficient string comparison |
|
1893
|
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
=item * Generates an if/elsif/else chain |
|
1895
|
|
|
|
|
|
|
|
|
1896
|
|
|
|
|
|
|
=back |
|
1897
|
|
|
|
|
|
|
|
|
1898
|
|
|
|
|
|
|
B |
|
1899
|
|
|
|
|
|
|
|
|
1900
|
|
|
|
|
|
|
$b->switch('code', [ |
|
1901
|
|
|
|
|
|
|
{ eq => 200, then => { return_pv => '"OK"' } }, |
|
1902
|
|
|
|
|
|
|
{ eq => 404, then => { return_pv => '"Not Found"' } }, |
|
1903
|
|
|
|
|
|
|
{ eq => 500, then => { return_pv => '"Server Error"' } }, |
|
1904
|
|
|
|
|
|
|
], { return_pv => '"Unknown"' }); |
|
1905
|
|
|
|
|
|
|
|
|
1906
|
|
|
|
|
|
|
For numeric comparisons, this generates code that: |
|
1907
|
|
|
|
|
|
|
|
|
1908
|
|
|
|
|
|
|
=over 4 |
|
1909
|
|
|
|
|
|
|
|
|
1910
|
|
|
|
|
|
|
=item * Caches C once at the start |
|
1911
|
|
|
|
|
|
|
|
|
1912
|
|
|
|
|
|
|
=item * Uses direct numeric comparison |
|
1913
|
|
|
|
|
|
|
|
|
1914
|
|
|
|
|
|
|
=back |
|
1915
|
|
|
|
|
|
|
|
|
1916
|
|
|
|
|
|
|
B |
|
1917
|
|
|
|
|
|
|
|
|
1918
|
|
|
|
|
|
|
$b->switch('score', [ |
|
1919
|
|
|
|
|
|
|
{ gte => 90, then => { return_pv => '"A"' } }, |
|
1920
|
|
|
|
|
|
|
{ gte => 80, then => { return_pv => '"B"' } }, |
|
1921
|
|
|
|
|
|
|
{ gte => 70, then => { return_pv => '"C"' } }, |
|
1922
|
|
|
|
|
|
|
{ gte => 60, then => { return_pv => '"D"' } }, |
|
1923
|
|
|
|
|
|
|
], { return_pv => '"F"' }); |
|
1924
|
|
|
|
|
|
|
|
|
1925
|
|
|
|
|
|
|
B |
|
1926
|
|
|
|
|
|
|
|
|
1927
|
|
|
|
|
|
|
$b->switch('status_code', [ |
|
1928
|
|
|
|
|
|
|
{ gte => 200, lte => 299, then => { return_pv => '"success"' } }, |
|
1929
|
|
|
|
|
|
|
{ gte => 300, lte => 399, then => { return_pv => '"redirect"' } }, |
|
1930
|
|
|
|
|
|
|
{ gte => 400, lte => 499, then => { return_pv => '"client_error"' } }, |
|
1931
|
|
|
|
|
|
|
{ gte => 500, lte => 599, then => { return_pv => '"server_error"' } }, |
|
1932
|
|
|
|
|
|
|
], { return_pv => '"unknown"' }); |
|
1933
|
|
|
|
|
|
|
|
|
1934
|
|
|
|
|
|
|
=back |
|
1935
|
|
|
|
|
|
|
|
|
1936
|
|
|
|
|
|
|
=head3 Complete Switch Example |
|
1937
|
|
|
|
|
|
|
|
|
1938
|
|
|
|
|
|
|
use XS::JIT; |
|
1939
|
|
|
|
|
|
|
use XS::JIT::Builder; |
|
1940
|
|
|
|
|
|
|
|
|
1941
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
1942
|
|
|
|
|
|
|
|
|
1943
|
|
|
|
|
|
|
$b->xs_function('get_type_id') |
|
1944
|
|
|
|
|
|
|
->xs_preamble |
|
1945
|
|
|
|
|
|
|
->declare_sv('type', 'ST(0)') |
|
1946
|
|
|
|
|
|
|
->switch('type', [ |
|
1947
|
|
|
|
|
|
|
{ eq => 'integer', then => { return_iv => '1' } }, |
|
1948
|
|
|
|
|
|
|
{ eq => 'string', then => { return_iv => '2' } }, |
|
1949
|
|
|
|
|
|
|
{ eq => 'float', then => { return_iv => '3' } }, |
|
1950
|
|
|
|
|
|
|
{ eq => 'boolean', then => { return_iv => '4' } }, |
|
1951
|
|
|
|
|
|
|
{ eq => 'array', then => { return_iv => '5' } }, |
|
1952
|
|
|
|
|
|
|
{ eq => 'hash', then => { return_iv => '6' } }, |
|
1953
|
|
|
|
|
|
|
], { return_iv => '0' }) |
|
1954
|
|
|
|
|
|
|
->xs_end; |
|
1955
|
|
|
|
|
|
|
|
|
1956
|
|
|
|
|
|
|
XS::JIT->compile( |
|
1957
|
|
|
|
|
|
|
code => $b->code, |
|
1958
|
|
|
|
|
|
|
name => 'TypeID', |
|
1959
|
|
|
|
|
|
|
functions => { |
|
1960
|
|
|
|
|
|
|
'TypeID::get' => { source => 'get_type_id', is_xs_native => 1 } |
|
1961
|
|
|
|
|
|
|
} |
|
1962
|
|
|
|
|
|
|
); |
|
1963
|
|
|
|
|
|
|
|
|
1964
|
|
|
|
|
|
|
say TypeID::get('integer'); # 1 |
|
1965
|
|
|
|
|
|
|
say TypeID::get('string'); # 2 |
|
1966
|
|
|
|
|
|
|
say TypeID::get('unknown'); # 0 |
|
1967
|
|
|
|
|
|
|
|
|
1968
|
|
|
|
|
|
|
=head3 Switch vs Conditional |
|
1969
|
|
|
|
|
|
|
|
|
1970
|
|
|
|
|
|
|
Use C when: |
|
1971
|
|
|
|
|
|
|
|
|
1972
|
|
|
|
|
|
|
=over 4 |
|
1973
|
|
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
=item * You have multiple conditions on the same key |
|
1975
|
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
=item * All cases compare the same variable |
|
1977
|
|
|
|
|
|
|
|
|
1978
|
|
|
|
|
|
|
=item * You want automatic optimization for string/numeric comparisons |
|
1979
|
|
|
|
|
|
|
|
|
1980
|
|
|
|
|
|
|
=item * You want cleaner syntax without repeating the key |
|
1981
|
|
|
|
|
|
|
|
|
1982
|
|
|
|
|
|
|
=back |
|
1983
|
|
|
|
|
|
|
|
|
1984
|
|
|
|
|
|
|
Use C when: |
|
1985
|
|
|
|
|
|
|
|
|
1986
|
|
|
|
|
|
|
=over 4 |
|
1987
|
|
|
|
|
|
|
|
|
1988
|
|
|
|
|
|
|
=item * Conditions involve different variables |
|
1989
|
|
|
|
|
|
|
|
|
1990
|
|
|
|
|
|
|
=item * You need complex nested AND/OR logic |
|
1991
|
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
=item * You're matching patterns with C/C |
|
1993
|
|
|
|
|
|
|
|
|
1994
|
|
|
|
|
|
|
=item * You prefer the Struct::Conditional format |
|
1995
|
|
|
|
|
|
|
|
|
1996
|
|
|
|
|
|
|
=back |
|
1997
|
|
|
|
|
|
|
|
|
1998
|
|
|
|
|
|
|
=head2 Bulk Code Generators |
|
1999
|
|
|
|
|
|
|
|
|
2000
|
|
|
|
|
|
|
These methods generate multiple related functions from a single declarative specification. |
|
2001
|
|
|
|
|
|
|
|
|
2002
|
|
|
|
|
|
|
=head3 Enum/Constant Generator |
|
2003
|
|
|
|
|
|
|
|
|
2004
|
|
|
|
|
|
|
=over 4 |
|
2005
|
|
|
|
|
|
|
|
|
2006
|
|
|
|
|
|
|
=item enum($name, \@values, [\%options]) |
|
2007
|
|
|
|
|
|
|
|
|
2008
|
|
|
|
|
|
|
Generate a set of related constants with validation functions. This is useful for |
|
2009
|
|
|
|
|
|
|
creating type-safe enumerated values with minimal boilerplate. |
|
2010
|
|
|
|
|
|
|
|
|
2011
|
|
|
|
|
|
|
$b->enum('Status', [qw(PENDING ACTIVE INACTIVE DELETED)]); |
|
2012
|
|
|
|
|
|
|
|
|
2013
|
|
|
|
|
|
|
B |
|
2014
|
|
|
|
|
|
|
|
|
2015
|
|
|
|
|
|
|
=over 4 |
|
2016
|
|
|
|
|
|
|
|
|
2017
|
|
|
|
|
|
|
=item * C - returns 0 |
|
2018
|
|
|
|
|
|
|
|
|
2019
|
|
|
|
|
|
|
=item * C - returns 1 |
|
2020
|
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
=item * C - returns 2 |
|
2022
|
|
|
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
=item * C - returns 3 |
|
2024
|
|
|
|
|
|
|
|
|
2025
|
|
|
|
|
|
|
=item * C - returns true if value is valid enum (0-3) |
|
2026
|
|
|
|
|
|
|
|
|
2027
|
|
|
|
|
|
|
=item * C - returns string name for numeric value |
|
2028
|
|
|
|
|
|
|
|
|
2029
|
|
|
|
|
|
|
=back |
|
2030
|
|
|
|
|
|
|
|
|
2031
|
|
|
|
|
|
|
B |
|
2032
|
|
|
|
|
|
|
|
|
2033
|
|
|
|
|
|
|
=over 4 |
|
2034
|
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
=item * C - Starting numeric value (default: 0) |
|
2036
|
|
|
|
|
|
|
|
|
2037
|
|
|
|
|
|
|
=item * C - Prefix for constant names (default: uc($name) . '_') |
|
2038
|
|
|
|
|
|
|
|
|
2039
|
|
|
|
|
|
|
=back |
|
2040
|
|
|
|
|
|
|
|
|
2041
|
|
|
|
|
|
|
# Custom start value |
|
2042
|
|
|
|
|
|
|
$b->enum('Priority', [qw(LOW MEDIUM HIGH CRITICAL)], { start => 1 }); |
|
2043
|
|
|
|
|
|
|
# Generates: PRIORITY_LOW => 1, PRIORITY_MEDIUM => 2, etc. |
|
2044
|
|
|
|
|
|
|
|
|
2045
|
|
|
|
|
|
|
# Custom prefix |
|
2046
|
|
|
|
|
|
|
$b->enum('Color', [qw(RED GREEN BLUE)], { prefix => 'CLR_' }); |
|
2047
|
|
|
|
|
|
|
# Generates: CLR_RED => 0, CLR_GREEN => 1, CLR_BLUE => 2 |
|
2048
|
|
|
|
|
|
|
|
|
2049
|
|
|
|
|
|
|
=item enum_functions($name, $package) |
|
2050
|
|
|
|
|
|
|
|
|
2051
|
|
|
|
|
|
|
Get a hashref of function definitions for use with Ccompile()>. |
|
2052
|
|
|
|
|
|
|
This returns the correct mapping for all functions generated by C. |
|
2053
|
|
|
|
|
|
|
|
|
2054
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
2055
|
|
|
|
|
|
|
$b->enum('Status', [qw(PENDING ACTIVE INACTIVE DELETED)]); |
|
2056
|
|
|
|
|
|
|
|
|
2057
|
|
|
|
|
|
|
my $functions = $b->enum_functions('Status', 'MyApp::Status'); |
|
2058
|
|
|
|
|
|
|
|
|
2059
|
|
|
|
|
|
|
XS::JIT->compile( |
|
2060
|
|
|
|
|
|
|
code => $b->code, |
|
2061
|
|
|
|
|
|
|
name => 'MyApp::Status', |
|
2062
|
|
|
|
|
|
|
functions => $functions, |
|
2063
|
|
|
|
|
|
|
); |
|
2064
|
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
# Now you can use: |
|
2066
|
|
|
|
|
|
|
print MyApp::Status::STATUS_PENDING(); # 0 |
|
2067
|
|
|
|
|
|
|
print MyApp::Status::STATUS_ACTIVE(); # 1 |
|
2068
|
|
|
|
|
|
|
print MyApp::Status::is_valid_status(2); # true |
|
2069
|
|
|
|
|
|
|
print MyApp::Status::status_name(1); # "ACTIVE" |
|
2070
|
|
|
|
|
|
|
|
|
2071
|
|
|
|
|
|
|
=back |
|
2072
|
|
|
|
|
|
|
|
|
2073
|
|
|
|
|
|
|
=head3 Complete Enum Example |
|
2074
|
|
|
|
|
|
|
|
|
2075
|
|
|
|
|
|
|
use XS::JIT; |
|
2076
|
|
|
|
|
|
|
use XS::JIT::Builder; |
|
2077
|
|
|
|
|
|
|
use File::Temp qw(tempdir); |
|
2078
|
|
|
|
|
|
|
|
|
2079
|
|
|
|
|
|
|
my $cache_dir = tempdir(CLEANUP => 1); |
|
2080
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
2081
|
|
|
|
|
|
|
|
|
2082
|
|
|
|
|
|
|
# Generate enum for HTTP status categories |
|
2083
|
|
|
|
|
|
|
$b->enum('HttpCategory', [qw(INFORMATIONAL SUCCESS REDIRECT CLIENT_ERROR SERVER_ERROR)]); |
|
2084
|
|
|
|
|
|
|
|
|
2085
|
|
|
|
|
|
|
XS::JIT->compile( |
|
2086
|
|
|
|
|
|
|
code => $b->code, |
|
2087
|
|
|
|
|
|
|
name => 'HTTP', |
|
2088
|
|
|
|
|
|
|
cache_dir => $cache_dir, |
|
2089
|
|
|
|
|
|
|
functions => $b->enum_functions('HttpCategory', 'HTTP'), |
|
2090
|
|
|
|
|
|
|
); |
|
2091
|
|
|
|
|
|
|
|
|
2092
|
|
|
|
|
|
|
# Use the generated functions |
|
2093
|
|
|
|
|
|
|
use constant { |
|
2094
|
|
|
|
|
|
|
HTTP_INFORMATIONAL => HTTP::HTTPCATEGORY_INFORMATIONAL(), |
|
2095
|
|
|
|
|
|
|
HTTP_SUCCESS => HTTP::HTTPCATEGORY_SUCCESS(), |
|
2096
|
|
|
|
|
|
|
HTTP_REDIRECT => HTTP::HTTPCATEGORY_REDIRECT(), |
|
2097
|
|
|
|
|
|
|
HTTP_CLIENT_ERROR => HTTP::HTTPCATEGORY_CLIENT_ERROR(), |
|
2098
|
|
|
|
|
|
|
HTTP_SERVER_ERROR => HTTP::HTTPCATEGORY_SERVER_ERROR(), |
|
2099
|
|
|
|
|
|
|
}; |
|
2100
|
|
|
|
|
|
|
|
|
2101
|
|
|
|
|
|
|
sub categorize_status { |
|
2102
|
|
|
|
|
|
|
my ($code) = @_; |
|
2103
|
|
|
|
|
|
|
return HTTP_INFORMATIONAL if $code >= 100 && $code < 200; |
|
2104
|
|
|
|
|
|
|
return HTTP_SUCCESS if $code >= 200 && $code < 300; |
|
2105
|
|
|
|
|
|
|
return HTTP_REDIRECT if $code >= 300 && $code < 400; |
|
2106
|
|
|
|
|
|
|
return HTTP_CLIENT_ERROR if $code >= 400 && $code < 500; |
|
2107
|
|
|
|
|
|
|
return HTTP_SERVER_ERROR if $code >= 500 && $code < 600; |
|
2108
|
|
|
|
|
|
|
return -1; |
|
2109
|
|
|
|
|
|
|
} |
|
2110
|
|
|
|
|
|
|
|
|
2111
|
|
|
|
|
|
|
my $cat = categorize_status(404); |
|
2112
|
|
|
|
|
|
|
print HTTP::is_valid_httpcategory($cat) ? "Valid" : "Invalid"; # "Valid" |
|
2113
|
|
|
|
|
|
|
print HTTP::httpcategory_name($cat); # "CLIENT_ERROR" |
|
2114
|
|
|
|
|
|
|
|
|
2115
|
|
|
|
|
|
|
=head3 Enum Use Cases |
|
2116
|
|
|
|
|
|
|
|
|
2117
|
|
|
|
|
|
|
=over 4 |
|
2118
|
|
|
|
|
|
|
|
|
2119
|
|
|
|
|
|
|
=item * B - Track record states (pending, active, deleted) |
|
2120
|
|
|
|
|
|
|
|
|
2121
|
|
|
|
|
|
|
=item * B - Define valid states with validation |
|
2122
|
|
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
=item * B - Type-safe config values |
|
2124
|
|
|
|
|
|
|
|
|
2125
|
|
|
|
|
|
|
=item * B - HTTP status codes, error codes, message types |
|
2126
|
|
|
|
|
|
|
|
|
2127
|
|
|
|
|
|
|
=back |
|
2128
|
|
|
|
|
|
|
|
|
2129
|
|
|
|
|
|
|
=head3 Memoization Wrapper |
|
2130
|
|
|
|
|
|
|
|
|
2131
|
|
|
|
|
|
|
Generate cached versions of expensive methods. The cache is stored in an object |
|
2132
|
|
|
|
|
|
|
attribute and can be cleared at any time. |
|
2133
|
|
|
|
|
|
|
|
|
2134
|
|
|
|
|
|
|
=over 4 |
|
2135
|
|
|
|
|
|
|
|
|
2136
|
|
|
|
|
|
|
=item memoize($func_name, [\%options]) |
|
2137
|
|
|
|
|
|
|
|
|
2138
|
|
|
|
|
|
|
Generate a memoized wrapper for a method. The original method must be renamed |
|
2139
|
|
|
|
|
|
|
to C<_orig_$func_name> before compilation. The wrapper checks a cache before |
|
2140
|
|
|
|
|
|
|
calling the original, stores results, and optionally supports TTL expiration. |
|
2141
|
|
|
|
|
|
|
|
|
2142
|
|
|
|
|
|
|
$b->memoize('expensive_calc'); |
|
2143
|
|
|
|
|
|
|
|
|
2144
|
|
|
|
|
|
|
# With options |
|
2145
|
|
|
|
|
|
|
$b->memoize('fetch_data', { |
|
2146
|
|
|
|
|
|
|
cache => '_fetch_cache', # custom cache attribute name |
|
2147
|
|
|
|
|
|
|
ttl => 300, # expire after 300 seconds |
|
2148
|
|
|
|
|
|
|
}); |
|
2149
|
|
|
|
|
|
|
|
|
2150
|
|
|
|
|
|
|
B |
|
2151
|
|
|
|
|
|
|
|
|
2152
|
|
|
|
|
|
|
=over 4 |
|
2153
|
|
|
|
|
|
|
|
|
2154
|
|
|
|
|
|
|
=item * C<$func_name()> - The memoized wrapper that checks cache first |
|
2155
|
|
|
|
|
|
|
|
|
2156
|
|
|
|
|
|
|
=item * C - Clears the cache for this function |
|
2157
|
|
|
|
|
|
|
|
|
2158
|
|
|
|
|
|
|
=back |
|
2159
|
|
|
|
|
|
|
|
|
2160
|
|
|
|
|
|
|
B |
|
2161
|
|
|
|
|
|
|
|
|
2162
|
|
|
|
|
|
|
=over 4 |
|
2163
|
|
|
|
|
|
|
|
|
2164
|
|
|
|
|
|
|
=item * C - Name of the hash attribute to store cache (default: '_memoize_cache') |
|
2165
|
|
|
|
|
|
|
|
|
2166
|
|
|
|
|
|
|
=item * C - Time-to-live in seconds. If set, cached values expire after this duration. |
|
2167
|
|
|
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
=back |
|
2169
|
|
|
|
|
|
|
|
|
2170
|
|
|
|
|
|
|
B |
|
2171
|
|
|
|
|
|
|
|
|
2172
|
|
|
|
|
|
|
The cache key is built by concatenating all arguments (except $self) with |
|
2173
|
|
|
|
|
|
|
ASCII field separator (0x1C). This handles most argument types correctly. |
|
2174
|
|
|
|
|
|
|
|
|
2175
|
|
|
|
|
|
|
B |
|
2176
|
|
|
|
|
|
|
|
|
2177
|
|
|
|
|
|
|
=over 4 |
|
2178
|
|
|
|
|
|
|
|
|
2179
|
|
|
|
|
|
|
=item 1. Arguments are joined to form a cache key |
|
2180
|
|
|
|
|
|
|
|
|
2181
|
|
|
|
|
|
|
=item 2. Cache is checked for existing value |
|
2182
|
|
|
|
|
|
|
|
|
2183
|
|
|
|
|
|
|
=item 3. If TTL is set, timestamp is verified |
|
2184
|
|
|
|
|
|
|
|
|
2185
|
|
|
|
|
|
|
=item 4. On cache miss, calls C<$self-E_orig_$func_name(@args)> |
|
2186
|
|
|
|
|
|
|
|
|
2187
|
|
|
|
|
|
|
=item 5. Result is stored in cache and returned |
|
2188
|
|
|
|
|
|
|
|
|
2189
|
|
|
|
|
|
|
=back |
|
2190
|
|
|
|
|
|
|
|
|
2191
|
|
|
|
|
|
|
=item memoize_functions($func_name, $package) |
|
2192
|
|
|
|
|
|
|
|
|
2193
|
|
|
|
|
|
|
Get a hashref of function definitions for use with Ccompile()>. |
|
2194
|
|
|
|
|
|
|
|
|
2195
|
|
|
|
|
|
|
my $functions = $b->memoize_functions('expensive_calc', 'MyClass'); |
|
2196
|
|
|
|
|
|
|
|
|
2197
|
|
|
|
|
|
|
XS::JIT->compile( |
|
2198
|
|
|
|
|
|
|
code => $b->code, |
|
2199
|
|
|
|
|
|
|
name => 'MyClass', |
|
2200
|
|
|
|
|
|
|
functions => $functions, |
|
2201
|
|
|
|
|
|
|
); |
|
2202
|
|
|
|
|
|
|
|
|
2203
|
|
|
|
|
|
|
=back |
|
2204
|
|
|
|
|
|
|
|
|
2205
|
|
|
|
|
|
|
=head3 Complete Memoization Example |
|
2206
|
|
|
|
|
|
|
|
|
2207
|
|
|
|
|
|
|
use XS::JIT; |
|
2208
|
|
|
|
|
|
|
use XS::JIT::Builder; |
|
2209
|
|
|
|
|
|
|
use File::Temp qw(tempdir); |
|
2210
|
|
|
|
|
|
|
|
|
2211
|
|
|
|
|
|
|
my $cache_dir = tempdir(CLEANUP => 1); |
|
2212
|
|
|
|
|
|
|
my $b = XS::JIT::Builder->new; |
|
2213
|
|
|
|
|
|
|
|
|
2214
|
|
|
|
|
|
|
# Generate memoized wrapper for 'compute' method |
|
2215
|
|
|
|
|
|
|
$b->memoize('compute', { ttl => 60 }); # 60 second TTL |
|
2216
|
|
|
|
|
|
|
|
|
2217
|
|
|
|
|
|
|
XS::JIT->compile( |
|
2218
|
|
|
|
|
|
|
code => $b->code, |
|
2219
|
|
|
|
|
|
|
name => 'Calculator', |
|
2220
|
|
|
|
|
|
|
cache_dir => $cache_dir, |
|
2221
|
|
|
|
|
|
|
functions => $b->memoize_functions('compute', 'Calculator'), |
|
2222
|
|
|
|
|
|
|
); |
|
2223
|
|
|
|
|
|
|
|
|
2224
|
|
|
|
|
|
|
package Calculator; |
|
2225
|
|
|
|
|
|
|
|
|
2226
|
|
|
|
|
|
|
# The original method - renamed with _orig_ prefix |
|
2227
|
|
|
|
|
|
|
sub _orig_compute { |
|
2228
|
|
|
|
|
|
|
my ($self, $x, $y) = @_; |
|
2229
|
|
|
|
|
|
|
# Expensive calculation... |
|
2230
|
|
|
|
|
|
|
sleep 1; # simulate slow operation |
|
2231
|
|
|
|
|
|
|
return $x * $y; |
|
2232
|
|
|
|
|
|
|
} |
|
2233
|
|
|
|
|
|
|
|
|
2234
|
|
|
|
|
|
|
sub new { bless {}, shift } |
|
2235
|
|
|
|
|
|
|
|
|
2236
|
|
|
|
|
|
|
package main; |
|
2237
|
|
|
|
|
|
|
|
|
2238
|
|
|
|
|
|
|
my $calc = Calculator->new; |
|
2239
|
|
|
|
|
|
|
|
|
2240
|
|
|
|
|
|
|
# First call - slow (calls _orig_compute) |
|
2241
|
|
|
|
|
|
|
my $result1 = $calc->compute(6, 7); # 42 |
|
2242
|
|
|
|
|
|
|
|
|
2243
|
|
|
|
|
|
|
# Second call - instant (from cache) |
|
2244
|
|
|
|
|
|
|
my $result2 = $calc->compute(6, 7); # 42 (cached) |
|
2245
|
|
|
|
|
|
|
|
|
2246
|
|
|
|
|
|
|
# Different args - slow again |
|
2247
|
|
|
|
|
|
|
my $result3 = $calc->compute(3, 4); # 12 |
|
2248
|
|
|
|
|
|
|
|
|
2249
|
|
|
|
|
|
|
# Clear cache |
|
2250
|
|
|
|
|
|
|
$calc->clear_compute_cache; |
|
2251
|
|
|
|
|
|
|
|
|
2252
|
|
|
|
|
|
|
# Next call will be slow again |
|
2253
|
|
|
|
|
|
|
my $result4 = $calc->compute(6, 7); # 42 (recalculated) |
|
2254
|
|
|
|
|
|
|
|
|
2255
|
|
|
|
|
|
|
=head3 Memoization Use Cases |
|
2256
|
|
|
|
|
|
|
|
|
2257
|
|
|
|
|
|
|
=over 4 |
|
2258
|
|
|
|
|
|
|
|
|
2259
|
|
|
|
|
|
|
=item * B - Cache expensive DB lookups |
|
2260
|
|
|
|
|
|
|
|
|
2261
|
|
|
|
|
|
|
=item * B - Cache external API calls with TTL |
|
2262
|
|
|
|
|
|
|
|
|
2263
|
|
|
|
|
|
|
=item * B - Cache results of CPU-intensive calculations |
|
2264
|
|
|
|
|
|
|
|
|
2265
|
|
|
|
|
|
|
=item * B - Cache parsed config values |
|
2266
|
|
|
|
|
|
|
|
|
2267
|
|
|
|
|
|
|
=back |
|
2268
|
|
|
|
|
|
|
|
|
2269
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
2270
|
|
|
|
|
|
|
|
|
2271
|
|
|
|
|
|
|
L - The main JIT compiler module |
|
2272
|
|
|
|
|
|
|
|
|
2273
|
|
|
|
|
|
|
The C API is available in F for direct use from XS code. |
|
2274
|
|
|
|
|
|
|
|
|
2275
|
|
|
|
|
|
|
=head1 AUTHOR |
|
2276
|
|
|
|
|
|
|
|
|
2277
|
|
|
|
|
|
|
LNATION |
|
2278
|
|
|
|
|
|
|
|
|
2279
|
|
|
|
|
|
|
=head1 LICENSE |
|
2280
|
|
|
|
|
|
|
|
|
2281
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
|
2282
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
|
2283
|
|
|
|
|
|
|
|
|
2284
|
|
|
|
|
|
|
=cut |
|
2285
|
|
|
|
|
|
|
|
|
2286
|
|
|
|
|
|
|
1; |