File Coverage

blib/lib/B/Stackobj.pm
Criterion Covered Total %
statement 16 201 7.9
branch 0 98 0.0
condition 0 36 0.0
subroutine 6 47 12.7
pod 0 21 0.0
total 22 403 5.4


line stmt bran cond sub pod time code
1             # Stackobj.pm
2             #
3             # Copyright (c) 1996 Malcolm Beattie
4             # Copyright (c) 2010 Reini Urban
5             # Copyright (c) 2012, 2013, 2014, 2015 cPanel Inc
6             #
7             # You may distribute under the terms of either the GNU General Public
8             # License or the Artistic License, as specified in the README file.
9             #
10             package B::Stackobj;
11              
12             our $VERSION = '1.12_01';
13              
14 14     14   46 use Exporter ();
  14         14  
  14         799  
15             @ISA = qw(Exporter);
16             our @EXPORT_OK = qw(set_callback T_UNKNOWN T_NUM T_INT T_STR VALID_UNSIGNED
17             VALID_INT VALID_NUM VALID_STR VALID_SV REGISTER TEMPORARY);
18             our %EXPORT_TAGS = (
19             types => [qw(T_UNKNOWN T_NUM T_INT T_STR)],
20             flags => [
21             qw(VALID_INT VALID_NUM VALID_STR VALID_SV
22             VALID_UNSIGNED REGISTER TEMPORARY)
23             ]
24             );
25              
26 14     14   44 use strict;
  14         16  
  14         276  
27 14     14   47 use B qw(SVf_IOK SVf_NOK SVf_IVisUV SVf_ROK SVf_POK);
  14         19  
  14         696  
28 14     14   45 use B::C qw(ivx nvx);
  14         11  
  14         464  
29 14     14   50 use Config;
  14         18  
  14         30675  
30              
31             # Types
32             sub T_UNKNOWN () { 0 }
33             sub T_INT () { 1 }
34             sub T_NUM () { 2 }
35             sub T_STR () { 3 }
36             sub T_SPECIAL () { 4 }
37              
38             # Flags
39             sub VALID_INT () { 0x01 }
40             sub VALID_UNSIGNED () { 0x02 }
41             sub VALID_NUM () { 0x04 }
42             sub VALID_STR () { 0x08 }
43             sub VALID_SV () { 0x10 }
44             sub REGISTER () { 0x20 } # no implicit write-back when calling subs
45             sub TEMPORARY () { 0x40 } # no implicit write-back needed at all
46             sub SAVE_INT () { 0x80 } # if int part needs to be saved at all
47             sub SAVE_NUM () { 0x100 } # if num part needs to be saved at all
48             sub SAVE_STR () { 0x200 } # if str part needs to be saved at all
49              
50             # no backtraces to avoid compiler pollution
51             #use Carp qw(confess);
52             sub confess {
53 0 0   0 0 0 if (exists &Carp::confess) {
54 0         0 goto &Carp::confess;
55             } else {
56 0         0 die @_."\n";
57             }
58             }
59              
60             #
61             # Callback for runtime code generation
62             #
63              
64             my $runtime_callback = sub { confess "set_callback not yet called" };
65 14     14 0 2751 sub set_callback (&) { $runtime_callback = shift }
66 0     0 0   sub runtime { &$runtime_callback(@_) }
67              
68             #
69             # Methods
70             #
71              
72             # The stack holds generally only the string ($sv->save) representation of the B object,
73             # for the types sv, int, double, numeric and sometimes bool.
74             # Special subclasses keep the B obj, like Const
75              
76 0     0 0   sub write_back { confess "stack object does not implement write_back" }
77              
78             sub invalidate {
79 0     0 0   shift->{flags} &= ~( VALID_INT | VALID_UNSIGNED | VALID_NUM | VALID_STR );
80             }
81              
82             sub invalidate_int {
83 0     0 0   shift->{flags} &= ~( VALID_INT | VALID_UNSIGNED );
84             }
85              
86             sub invalidate_double {
87 0     0 0   shift->{flags} &= ~( VALID_NUM );
88             }
89              
90             sub invalidate_str {
91 0     0 0   shift->{flags} &= ~( VALID_STR );
92             }
93              
94             sub as_sv {
95 0     0 0   my $obj = shift;
96 0 0         if ( !( $obj->{flags} & VALID_SV ) ) {
97 0           $obj->write_back;
98 0           $obj->{flags} |= VALID_SV;
99             }
100 0           return $obj->{sv};
101             }
102              
103             sub as_obj {
104 0     0 0   return shift->{obj};
105             }
106              
107             sub as_int {
108 0     0 0   my $obj = shift;
109 0 0         if ( !( $obj->{flags} & VALID_INT ) ) {
110 0           $obj->load_int;
111 0           $obj->{flags} |= VALID_INT | SAVE_INT;
112             }
113 0           return $obj->{iv};
114             }
115              
116             sub as_double {
117 0     0 0   my $obj = shift;
118 0 0         if ( !( $obj->{flags} & VALID_NUM ) ) {
119 0           $obj->load_double;
120 0           $obj->{flags} |= VALID_NUM | SAVE_NUM;
121             }
122 0           return $obj->{nv};
123             }
124              
125             sub as_str {
126 0     0 0   my $obj = shift;
127 0 0         if ( !( $obj->{flags} & VALID_STR ) ) {
128 0           $obj->load_str;
129 0           $obj->{flags} |= VALID_STR | SAVE_STR;
130             }
131 0           return $obj->{sv};
132             }
133              
134             sub as_numeric {
135 0     0 0   my $obj = shift;
136 0 0         return $obj->{type} == T_INT ? $obj->as_int : $obj->as_double;
137             }
138              
139             sub as_bool {
140 0     0 0   my $obj = shift;
141 0 0         if ( $obj->{flags} & VALID_INT ) {
142 0           return $obj->{iv};
143             }
144 0 0         if ( $obj->{flags} & VALID_NUM ) {
145 0           return $obj->{nv};
146             }
147 0           return sprintf( "(SvTRUE(%s))", $obj->as_sv );
148             }
149              
150             #
151             # Debugging methods
152             #
153             sub peek {
154 0     0 0   my $obj = shift;
155 0           my $type = $obj->{type};
156 0           my $flags = $obj->{flags};
157 0           my @flags;
158 0 0         if ( $type == T_UNKNOWN ) {
    0          
    0          
    0          
159 0           $type = "T_UNKNOWN";
160             }
161             elsif ( $type == T_INT ) {
162 0           $type = "T_INT";
163             }
164             elsif ( $type == T_NUM ) {
165 0           $type = "T_NUM";
166             }
167             elsif ( $type == T_STR ) {
168 0           $type = "T_STR";
169             }
170             else {
171 0           $type = "(illegal type $type)";
172             }
173 0 0         push( @flags, "VALID_INT" ) if $flags & VALID_INT;
174 0 0         push( @flags, "VALID_NUM" ) if $flags & VALID_NUM;
175 0 0         push( @flags, "VALID_STR" ) if $flags & VALID_STR;
176 0 0         push( @flags, "VALID_SV" ) if $flags & VALID_SV;
177 0 0         push( @flags, "REGISTER" ) if $flags & REGISTER;
178 0 0         push( @flags, "TEMPORARY" ) if $flags & TEMPORARY;
179 0 0         @flags = ("none") unless @flags;
180 0           return sprintf( "%s type=$type flags=%s sv=$obj->{sv} iv=$obj->{iv} nv=$obj->{nv}",
181             B::class($obj), join( "|", @flags ) );
182             }
183              
184             sub minipeek {
185 0     0 0   my $obj = shift;
186 0           my $type = $obj->{type};
187 0           my $flags = $obj->{flags};
188 0 0 0       if ( $type == T_INT || $flags & VALID_INT ) {
    0 0        
189 0           return $obj->{iv};
190             }
191             elsif ( $type == T_NUM || $flags & VALID_NUM ) {
192 0           return $obj->{nv};
193             }
194             else {
195 0           return $obj->{sv};
196             }
197             }
198              
199             #
200             # Caller needs to ensure that set_int, set_double,
201             # set_numeric and set_sv are only invoked on legal lvalues.
202             #
203             sub set_int {
204 0     0 0   my ( $obj, $expr, $unsigned ) = @_;
205 0           my $sval;
206             # bullshit detector for non numeric expr, expr 'lnv0 + rnv0'
207 0 0         if ($expr =~ /[ a-dfzA-DF-Z]/) { # looks not like number
208 0           $sval = $expr;
209             } else {
210 0           $sval = B::C::ivx($expr);
211 0 0 0       $sval = $expr if $sval eq '0' and $expr;
212             }
213              
214 0           runtime("$obj->{iv} = $sval;");
215 0           $obj->{flags} &= ~( VALID_SV | VALID_NUM );
216 0           $obj->{flags} |= VALID_INT | SAVE_INT;
217 0 0         $obj->{flags} |= VALID_UNSIGNED if $unsigned;
218             }
219              
220             sub set_double {
221 0     0 0   my ( $obj, $expr ) = @_;
222 0           my $sval;
223 0 0         if ($expr =~ /^-?(Inf|NaN)$/i) {
    0          
224 0           $sval = B::C::nvx($expr);
225 0 0 0       $sval = $expr if $sval eq '0' and $expr;
226             # bullshit detector for non numeric expr, expr 'lnv0 + rnv0'
227             } elsif ($expr =~ /[ a-dfzA-DF-Z]/) { # looks not like number
228 0           $sval = $expr;
229             } else {
230 0           $sval = B::C::nvx($expr);
231 0 0 0       $sval = $expr if $sval eq '0' and $expr;
232             }
233              
234 0           runtime("$obj->{nv} = $sval;");
235 0           $obj->{flags} &= ~( VALID_SV | VALID_INT );
236 0           $obj->{flags} |= VALID_NUM | SAVE_NUM;
237             }
238              
239             sub set_numeric {
240 0     0 0   my ( $obj, $expr ) = @_;
241 0 0         if ( $obj->{type} == T_INT ) {
242 0           $obj->set_int($expr);
243             }
244             else {
245 0           $obj->set_double($expr);
246             }
247             }
248              
249             sub set_sv {
250 0     0 0   my ( $obj, $expr ) = @_;
251 0           runtime("SvSetSV($obj->{sv}, $expr);");
252 0           $obj->invalidate;
253 0           $obj->{flags} |= VALID_SV;
254             }
255              
256             #
257             # Stackobj::Padsv
258             #
259              
260             @B::Stackobj::Padsv::ISA = 'B::Stackobj';
261              
262             sub B::Stackobj::Padsv::new {
263 0     0     my ( $class, $type, $extra_flags, $ix, $iname, $dname ) = @_;
264 0 0         $extra_flags |= SAVE_INT if $extra_flags & VALID_INT;
265 0 0         $extra_flags |= SAVE_NUM if $extra_flags & VALID_NUM;
266 0           bless {
267             type => $type,
268             flags => VALID_SV | $extra_flags,
269             targ => $ix,
270             sv => "PL_curpad[$ix]",
271             iv => "$iname",
272             nv => "$dname",
273             }, $class;
274             }
275              
276             sub B::Stackobj::Padsv::as_obj {
277 0     0     my $obj = shift;
278 0           my @c = comppadlist->ARRAY;
279 0           my @p = $c[1]->ARRAY;
280 0           return $p[ $obj->{targ} ];
281             }
282              
283             sub B::Stackobj::Padsv::load_int {
284 0     0     my $obj = shift;
285 0 0         if ( $obj->{flags} & VALID_NUM ) {
286 0           runtime("$obj->{iv} = $obj->{nv};");
287             }
288             else {
289 0           runtime("$obj->{iv} = SvIV($obj->{sv});");
290             }
291 0           $obj->{flags} |= VALID_INT | SAVE_INT;
292             }
293              
294             sub B::Stackobj::Padsv::load_double {
295 0     0     my $obj = shift;
296 0           $obj->write_back;
297 0           runtime("$obj->{nv} = SvNV($obj->{sv});");
298 0           $obj->{flags} |= VALID_NUM | SAVE_NUM;
299             }
300              
301             sub B::Stackobj::Padsv::load_str {
302 0     0     my $obj = shift;
303 0           $obj->write_back;
304 0           $obj->{flags} |= VALID_STR | SAVE_STR;
305             }
306              
307             sub B::Stackobj::Padsv::save_int {
308 0     0     my $obj = shift;
309 0           return $obj->{flags} & SAVE_INT;
310             }
311              
312             sub B::Stackobj::Padsv::save_double {
313 0     0     my $obj = shift;
314 0           return $obj->{flags} & SAVE_NUM;
315             }
316              
317             sub B::Stackobj::Padsv::save_str {
318 0     0     my $obj = shift;
319 0           return $obj->{flags} & SAVE_STR;
320             }
321              
322             sub B::Stackobj::Padsv::write_back {
323 0     0     my $obj = shift;
324 0           my $flags = $obj->{flags};
325 0 0         return if $flags & VALID_SV;
326 0 0         if ( $flags & VALID_INT ) {
    0          
    0          
327 0 0         if ( $flags & VALID_UNSIGNED ) {
328 0           runtime("sv_setuv($obj->{sv}, $obj->{iv});");
329             }
330             else {
331 0           runtime("sv_setiv($obj->{sv}, $obj->{iv});");
332             }
333             }
334             elsif ( $flags & VALID_NUM ) {
335 0           runtime("sv_setnv($obj->{sv}, $obj->{nv});");
336             }
337             elsif ( $flags & VALID_STR ) {
338             ;
339             }
340             else {
341 0           confess "write_back failed for lexical @{[$obj->peek]}\n";
  0            
342             }
343 0           $obj->{flags} |= VALID_SV;
344             }
345              
346             #
347             # Stackobj::Const
348             #
349              
350             @B::Stackobj::Const::ISA = 'B::Stackobj';
351              
352             sub B::Stackobj::Const::new {
353 0     0     my ( $class, $sv ) = @_;
354 0           my $obj = bless {
355             flags => 0,
356             sv => $sv, # holds the SV object until write_back happens
357             obj => $sv
358             }, $class;
359 0 0         if ( ref($sv) eq "B::SPECIAL" ) {
360 0           $obj->{type} = T_SPECIAL;
361             }
362             else {
363 0           my $svflags = $sv->FLAGS;
364 0 0         if ( $svflags & SVf_IOK ) {
    0          
    0          
365 0           $obj->{flags} = VALID_INT | VALID_NUM;
366 0           $obj->{type} = T_INT;
367 0 0         if ( $svflags & SVf_IVisUV ) {
368 0           $obj->{flags} |= VALID_UNSIGNED;
369 0           $obj->{nv} = $obj->{iv} = $sv->UVX;
370             }
371             else {
372 0           $obj->{nv} = $obj->{iv} = $sv->IV;
373             }
374             }
375             elsif ( $svflags & SVf_NOK ) {
376 0           $obj->{flags} = VALID_INT | VALID_NUM;
377 0           $obj->{type} = T_NUM;
378 0           $obj->{iv} = $obj->{nv} = $sv->NV;
379             }
380             elsif ( $svflags & SVf_POK ) {
381 0           $obj->{flags} = VALID_STR;
382 0           $obj->{type} = T_STR;
383 0           $obj->{sv} = $sv;
384             }
385             else {
386 0           $obj->{type} = T_UNKNOWN;
387             }
388             }
389 0           return $obj;
390             }
391              
392             sub B::Stackobj::Const::write_back {
393 0     0     my $obj = shift;
394 0 0         return if $obj->{flags} & VALID_SV;
395              
396             # Save the SV object and replace $obj->{sv} by its C source code name
397 0           $obj->{sv} = $obj->{obj}->save;
398 0           $obj->{flags} |= VALID_SV | VALID_INT | VALID_NUM;
399             }
400              
401             sub B::Stackobj::Const::load_int {
402 0     0     my $obj = shift;
403 0 0 0       if ( ref( $obj->{obj} ) eq "B::RV" or ($] >= 5.011 and $obj->{obj}->FLAGS & SVf_ROK)) {
      0        
404 0           $obj->{iv} = int( $obj->{obj}->RV->PV );
405             }
406             else {
407 0           $obj->{iv} = int( $obj->{obj}->PV );
408             }
409 0           $obj->{flags} |= VALID_INT;
410             }
411              
412             sub B::Stackobj::Const::load_double {
413 0     0     my $obj = shift;
414 0 0 0       if ( ref( $obj->{obj} ) eq "B::RV" or ($] >= 5.011 and $obj->{obj}->FLAGS & SVf_ROK)) {
      0        
415 0           $obj->{nv} = $obj->{obj}->RV->PV + 0.0;
416             }
417             else {
418 0           $obj->{nv} = $obj->{obj}->PV + 0.0;
419             }
420 0           $obj->{flags} |= VALID_NUM;
421             }
422              
423             sub B::Stackobj::Const::load_str {
424 0     0     my $obj = shift;
425 0 0 0       if ( ref( $obj->{obj} ) eq "B::RV" or ($] >= 5.011 and $obj->{obj}->FLAGS & SVf_ROK)) {
      0        
426 0           $obj->{sv} = $obj->{obj}->RV;
427             }
428             else {
429 0           $obj->{sv} = $obj->{obj};
430             }
431 0           $obj->{flags} |= VALID_STR;
432             }
433              
434       0     sub B::Stackobj::Const::invalidate { }
435              
436             #
437             # Stackobj::Bool
438             #
439             ;
440             @B::Stackobj::Bool::ISA = 'B::Stackobj';
441              
442             sub B::Stackobj::Bool::new {
443 0     0     my ( $class, $preg ) = @_;
444 0           my $obj = bless {
445             type => T_INT,
446             flags => VALID_INT | VALID_NUM,
447             iv => $$preg,
448             nv => $$preg,
449             obj => $preg # this holds our ref to the pseudo-reg
450             }, $class;
451 0           return $obj;
452             }
453              
454             sub B::Stackobj::Bool::write_back {
455 0     0     my $obj = shift;
456 0 0         return if $obj->{flags} & VALID_SV;
457 0           $obj->{sv} = "($obj->{iv} ? &PL_sv_yes : &PL_sv_no)";
458 0           $obj->{flags} |= VALID_SV;
459             }
460              
461             # XXX Might want to handle as_double/set_double/load_double?
462              
463       0     sub B::Stackobj::Bool::invalidate { }
464              
465             #
466             # Stackobj::Aelem
467             #
468              
469             @B::Stackobj::Aelem::ISA = 'B::Stackobj';
470              
471             sub B::Stackobj::Aelem::new {
472 0     0     my ( $class, $av, $ix, $lvalue ) = @_;
473 0           my $sv;
474             # pop ix before av
475 0 0 0       if ($av eq 'POPs' and $ix eq 'POPi') {
    0          
476 0           $sv = "({ int _ix = POPi; _ix >= 0 ? AvARRAY(POPs)[_ix] : *av_fetch((AV*)POPs, _ix, $lvalue); })";
477             } elsif ($ix =~ /^-?[\d\.]+$/) {
478 0           $sv = "AvARRAY($av)[$ix]";
479             } else {
480 0           $sv = "($ix >= 0 ? AvARRAY($av)[$ix] : *av_fetch((AV*)$av, $ix, $lvalue))";
481             }
482 0           my $obj = bless {
483             type => T_UNKNOWN,
484             flags => VALID_INT | VALID_NUM | VALID_SV,
485             iv => "SvIVX($sv)",
486             nv => "SvNVX($sv)",
487             sv => "$sv",
488             lvalue => $lvalue,
489             }, $class;
490 0           return $obj;
491             }
492              
493             sub B::Stackobj::Aelem::write_back {
494 0     0     my $obj = shift;
495 0           $obj->{flags} |= VALID_SV | VALID_INT | VALID_NUM | VALID_STR;
496             }
497              
498       0     sub B::Stackobj::Aelem::invalidate { }
499              
500             1;
501              
502             __END__