File Coverage

blib/lib/Nice/Try.pm
Criterion Covered Total %
statement 482 618 77.9
branch 219 400 54.7
condition 99 172 57.5
subroutine 36 45 80.0
pod 4 7 57.1
total 840 1242 67.6


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## A real Try Catch Block Implementation Using Perl Filter - ~/lib/Nice/Try.pm
3             ## Version v1.3.2
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest
6             ## Created 2020/05/17
7             ## Modified 2023/01/13
8             ## All rights reserved
9             ##
10             ## This program is free software; you can redistribute it and/or modify it
11             ## under the same terms as Perl itself.
12             ##----------------------------------------------------------------------------
13             package Nice::Try;
14             BEGIN
15             {
16 24     24   339 require 5.16.0;
17 24     24   2621773 use strict;
  24         309  
  24         759  
18 24     24   124 use warnings;
  24         54  
  24         691  
19 24     24   140 use warnings::register;
  24         50  
  24         3788  
20 24         2903 use vars qw(
21             $CATCH $DIED $EXCEPTION $FINALLY $HAS_CATCH @RETVAL $SENTINEL $TRY $WANTARRAY
22             $VERSION $ERROR
23 24     24   171 );
  24         57  
24             # XXX Only for debugging
25             # use Devel::Confess;
26 24     24   13927 use PPI;
  24         2995185  
  24         1590  
27 24     24   12790 use Filter::Util::Call;
  24         20234  
  24         1811  
28 24     24   195 use Scalar::Util ();
  24         57  
  24         433  
29 24     24   153 use List::Util ();
  24         71  
  24         438  
30 24     24   12033 use Want ();
  24         45293  
  24         1711  
31 24         97 our $VERSION = 'v1.3.2';
32 24         61 our $ERROR;
33 24         552 our( $CATCH, $DIED, $EXCEPTION, $FINALLY, $HAS_CATCH, @RETVAL, $SENTINEL, $TRY, $WANTARRAY );
34             }
35              
36 24     24   147 use strict;
  24         70  
  24         469  
37 24     24   149 use warnings;
  24         69  
  24         50489  
38              
39             # Taken from Try::Harder version 0.005
40             our $SENTINEL = bless( {} => __PACKAGE__ . '::SENTINEL' );
41              
42             sub import
43             {
44 25     25   933 my( $this, @arguments ) = @_ ;
45 25         72 my $class = CORE::caller();
46 25         180 my $hash = { @arguments };
47 25 50       153 $hash->{debug} = 0 if( !CORE::exists( $hash->{debug} ) );
48 25 50       149 $hash->{no_filter} = 0 if( !CORE::exists( $hash->{no_filter} ) );
49 25 50       82 $hash->{debug_code} = 0 if( !CORE::exists( $hash->{debug_code} ) );
50 25 50       80 $hash->{debug_dump} = 0 if( !CORE::exists( $hash->{debug_dump} ) );
51 25 50       79 $hash->{dont_want} = 0 if( !CORE::exists( $hash->{dont_want} ) );
52             # We check if we are running under tie and if so we cannot use Want features,
53             # because they would trigger a segmentation fault.
54 25         57 $hash->{is_tied} = 0;
55 25 50 33     740 if( $class->can( 'TIESCALAR' ) || $class->can( 'TIEHASH' ) || $class->can( 'TIEARRAY' ) )
      33        
56             {
57 0         0 $hash->{is_tied} = 1;
58             }
59 25         158 require overload;
60 25 50       147 $hash->{is_overloaded} = overload::Overloaded( $class ) ? 1 : 0;
61 25         1986 $hash->{no_context} = 0;
62             # 2021-05-17 (Jacques): the following was a bad idea as it was indiscriminate and
63             # would also affect use of caller outside of try-catch blocks
64             # *{"${class}::caller"} = \&{"Nice::Try::caller"};
65 25   33     265 filter_add( bless( $hash => ( ref( $this ) || $this ) ) );
66             }
67              
68             sub unimport
69             {
70 0     0   0 filter_del();
71             }
72              
73             sub caller($;$)
74             {
75 6     6 1 13 my $where = shift( @_ );
76 6         11 my $n = shift( @_ );
77             # Offsetting our internal call frames
78 6         20 my $map =
79             {
80             try => 3,
81             catch => 2,
82             finally => 5,
83             };
84 6 100       44 my @info = defined( $n ) ? CORE::caller( int( $n ) + $map->{ $where } ) : CORE::caller( 1 + $map->{ $where } );
85 6         41 return( @info );
86             }
87              
88 3     3 0 1694 sub caller_try { return( &Nice::Try::caller( try => @_ ) ); }
89              
90 1     1 0 1491 sub caller_catch { return( &Nice::Try::caller( catch => @_ ) ); }
91              
92 2     2 0 13 sub caller_finally { return( &Nice::Try::caller( finally => @_ ) ); }
93              
94             sub filter
95             {
96 43     43 1 78099 my( $self ) = @_ ;
97 43         86 my( $status, $last_line );
98 43         84 my $line = 0;
99 43         92 my $code = '';
100 43 50       332 if( $self->{no_filter} )
101             {
102 0         0 filter_del();
103 0         0 $status = 1;
104 0 0       0 $self->_message( 3, "Skiping filtering." ) if( $self->{debug} >= 3 );
105 0         0 return( $status );
106             }
107 43         364 while( $status = filter_read() )
108             {
109             # Error
110 2489 50       3738 if( $status < 0 )
111             {
112 0 0       0 $self->_message( 3, "An error occurred in fiilter, aborting." ) if( $self->{debug} >= 3 );
113 0         0 return( $status );
114             }
115 2489         2697 $line++;
116             # if( /^__(?:DATA|END)__/ )
117             # {
118             # $last_line = $_;
119             # last;
120             # }
121 2489         3177 $code .= $_;
122 2489         5189 $_ = '';
123             }
124 43 100       41797 return( $line ) if( !$line );
125 25 50       134 unless( $status < 0 )
126             {
127             # $self->_message( 5, "Processing at line $line code:\n$code" );
128             # 2021-06-05 (Jacques): fixes the issue No. 3
129             # Make sure there is at least a space at the beginning
130 25         212 $code = ' ' . $code;
131 25 50       144 $self->_message( 4, "Processing $line lines of code." ) if( $self->{debug} >= 4 );
132 25   50     393 my $doc = PPI::Document->new( \$code, readonly => 1 ) || die( "Unable to parse: ", PPI::Document->errstr, "\n$code\n" );
133             # Remove pod
134             # $doc->prune('PPI::Token::Pod');
135 25 50       2376326 $self->_browse( $doc ) if( $self->{debug_dump} );
136 25 100       167 if( $doc = $self->_parse( $doc ) )
137             {
138 21         234 $_ = $doc->serialize;
139             # $doc->save( "./dev/debug-parsed.pl" );
140             # $status = 1;
141             }
142             # Rollback
143             else
144             {
145             # $self->_message( 5, "Nothing found, restoring code to '$code'" );
146 4         1090 $_ = $code;
147             # $status = -1;
148             # filter_del();
149             }
150 25 50       119451 if( CORE::length( $last_line ) )
151             {
152 0         0 $_ .= $last_line;
153             }
154             }
155 25 50       61693 unless( $status <= 0 )
156             {
157 0         0 while( $status = filter_read() )
158             {
159 0         0 $self->_message( 4, "Reading more line: $_" );
160 0 0       0 return( $status ) if( $status < 0 );
161 0         0 $line++;
162             }
163             }
164             # $self->_message( 3, "Returning status '$line' with \$_ set to '$_'." );
165 25 50       121 if( $self->{debug_file} )
166             {
167 0 0       0 if( open( my $fh, ">$self->{debug_file}" ) )
168             {
169 0         0 binmode( $fh, ':utf8' );
170 0         0 print( $fh $_ );
171 0         0 close( $fh );
172             }
173             }
174             # filter_del();
175 25         23823 return( $line );
176             }
177              
178             sub implement
179             {
180 0     0 1 0 my $self = shift( @_ );
181 0         0 my $code = shift( @_ );
182 0 0 0     0 return( $code ) if( !CORE::defined( $code ) || !CORE::length( $code ) );
183 0 0       0 unless( ref( $self ) )
184             {
185 0 0 0     0 my $opts = ( !@_ || !defined( $_[0] ) )
    0          
    0          
186             ? {}
187             : ref( $_[0] ) eq 'HASH'
188             ? shift( @_ )
189             : !( @_ % 2 )
190             ? { @_ }
191             : {};
192 0         0 for( qw( debug no_context no_filter debug_code debug_dump debug_file dont_want is_tied is_overloaded ) )
193             {
194 0   0     0 $opts->{ $_ } //= 0;
195             }
196 0         0 $self = bless( $opts => $self );
197             }
198             # 2021-06-05 (Jacques): fixes the issue No. 3
199             # Make sure there is at least a space at the beginning
200 0         0 $code = ' ' . $code;
201 0 0       0 $self->_message( 4, "Processing ", CORE::length( $code ), " bytes of code." ) if( $self->{debug} >= 4 );
202 0   0     0 my $doc = PPI::Document->new( \$code, readonly => 1 ) || die( "Unable to parse: ", PPI::Document->errstr, "\n$code\n" );
203 0 0       0 $self->_browse( $doc ) if( $self->{debug_dump} );
204 0 0       0 if( $doc = $self->_parse( $doc ) )
205             {
206 0         0 $code = $doc->serialize;
207             }
208 0         0 return( $code );
209             }
210              
211             sub _browse
212             {
213 0     0   0 my $self = shift( @_ );
214 0         0 my $elem = shift( @_ );
215 0   0     0 my $level = shift( @_ ) || 0;
216 0 0       0 if( $self->{debug} >= 4 )
217             {
218 0         0 $self->_message( 4, "Checking code: ", $self->_serialize( $elem ) );
219 0         0 $self->_messagef( 4, "PPI element of class %s has children property '%s'.", $elem->class, $elem->{children} );
220             }
221 0 0       0 return if( !$elem->children );
222 0         0 foreach my $e ( $elem->elements )
223             {
224 0         0 printf( STDERR "%sElement: [%d] class %s, value %s\n", ( '.' x $level ), $e->line_number, $e->class, $e->content );
225 0 0 0     0 if( $e->can('children') && $e->children )
226             {
227 0         0 $self->_browse( $e, $level + 1 );
228             }
229             }
230             }
231              
232             sub _error
233             {
234 0     0   0 my $self = shift( @_ );
235 0 0       0 if( @_ )
236             {
237 0 0       0 my $txt = join( '', map( ref( $_ ) eq 'CODE' ? $_->() : $_, @_ ) );
238 0         0 $txt =~ s/[\015\012]+$//g;
239 0         0 $ERROR = $txt;
240 0 0       0 CORE::warn( "$txt\n" ) if( warnings::enabled );
241 0         0 return;
242             }
243 0         0 return( $ERROR );
244             }
245              
246             sub _message
247             {
248 0     0   0 my $self = shift( @_ );
249 0 0       0 my $level = $_[0] =~ /^\d+$/ ? shift( @_ ) : 0;
250 0 0       0 return if( $self->{debug} < $level );
251 0         0 my @data = @_;
252 0         0 my $stackFrame = 0;
253 0         0 my( $pkg, $file, $line, @otherInfo ) = CORE::caller( $stackFrame );
254 0         0 my $sub = ( CORE::caller( $stackFrame + 1 ) )[3];
255 0         0 my $sub2 = substr( $sub, rindex( $sub, '::' ) + 2 );
256 0 0       0 my $txt = "${pkg}::${sub2}( $self ) [$line]: " . join( '', map( ref( $_ ) eq 'CODE' ? $_->() : $_, @data ) );
257 0         0 $txt =~ s/\n$//gs;
258 0         0 $txt = '## ' . join( "\n## ", split( /\n/, $txt ) );
259 0         0 CORE::print( STDERR $txt, "\n" );
260             }
261              
262             sub _messagef
263             {
264 96     96   4800 my $self = shift( @_ );
265 96 50       584 my $level = $_[0] =~ /^\d+$/ ? shift( @_ ) : 0;
266 96 50       315 return if( $self->{debug} < $level );
267 0         0 my @data = @_;
268 0         0 my $stackFrame = 0;
269 0         0 my $fmt = shift( @data );
270 0         0 my( $pkg, $file, $line, @otherInfo ) = CORE::caller( $stackFrame );
271 0         0 my $sub = ( CORE::caller( $stackFrame + 1 ) )[3];
272 0         0 my $sub2 = substr( $sub, rindex( $sub, '::' ) + 2 );
273 0         0 for( @data )
274             {
275 0 0       0 next if( ref( $_ ) );
276 0         0 s/\b\%/\x{025}/g;
277             }
278 0 0       0 my $txt = "${pkg}::${sub2}( $self ) [$line]: " . sprintf( $fmt, map( ref( $_ ) eq 'CODE' ? $_->() : $_, @data ) );
279 0         0 $txt =~ s/\n$//gs;
280 0         0 $txt = '## ' . join( "\n## ", split( /\n/, $txt ) );
281 0         0 CORE::print( STDERR $txt, "\n" );
282             }
283              
284             sub _parse
285             {
286 267     267   493 my $self = shift( @_ );
287 267         420 my $elem = shift( @_ );
288 24     24   232 no warnings 'uninitialized';
  24         117  
  24         83384  
289 267 50 33     2050 if( !Scalar::Util::blessed( $elem ) || !$elem->isa( 'PPI::Node' ) )
290             {
291 0         0 return( $self->_error( "Element provided to parse is not a PPI::Node object" ) );
292             }
293            
294             my $ref = $elem->find(sub
295             {
296 25549     25549   440715 my( $top, $this ) = @_;
297 25549   100     43208 return( $this->class eq 'PPI::Statement' && substr( $this->content, 0, 3 ) eq 'try' );
298 267         2042 });
299 267 50       4720 return( $self->_error( "Failed to find any try-catch clause: $@" ) ) if( !defined( $ref ) );
300 267 50 66     948 $self->_messagef( 4, "Found %d match(es)", scalar( @$ref ) ) if( $ref && ref( $ref ) && $self->{debug} >= 4 );
      66        
301 267 100 66     1236 return if( !$ref || !scalar( @$ref ) );
302            
303             # 2020-09-13: PPI will return 2 or more consecutive try-catch block as 1 statement
304             # It does not tell them apart, so we need to post process the result to effectively search within for possible for other try-catch blocks and update the @$ref array consequently
305             # Array to contain the new version of the $ref array.
306 25         73 my $alt_ref = [];
307 25 50       119 $self->_message( 3, "Checking for consecutive try-catch blocks in results found by PPI" ) if( $self->{debug} >= 3 );
308 25         79 foreach my $this ( @$ref )
309             {
310 118         1228 my( @block_children ) = $this->children;
311 118 50       982 next if( !scalar( @block_children ) );
312 118         198 my $tmp_ref = [];
313             ## to contain all the nodes to move
314 118         184 my $tmp_nodes = [];
315 118         208 my $prev_sib = $block_children[0];
316 118         213 push( @$tmp_nodes, $prev_sib );
317 118         153 my $sib;
318 118         388 while( $sib = $prev_sib->next_sibling )
319             {
320             # We found a try-catch block. Move the buffer to $alt_ref
321 1538 100 100     38076 if( $sib->class eq 'PPI::Token::Word' && $sib->content eq 'try' )
322             {
323             # Look ahead for a block...
324 2         21 my $next = $sib->snext_sibling;
325 2 50 33     76 if( $next && $next->class eq 'PPI::Structure::Block' )
326             {
327 2 50       14 $self->_message( 3, "Found consecutive try-block." ) if( $self->{debug} >= 3 );
328             # Push the previous statement $st to the stack $alt_ref
329 2 50       5 $self->_messagef( 3, "Saving previous %d nodes collected.", scalar( @$tmp_nodes ) ) if( $self->{debug} >= 3 );
330 2         19 push( @$tmp_ref, $tmp_nodes );
331 2         6 $tmp_nodes = [];
332             }
333             }
334 1538         7063 push( @$tmp_nodes, $sib );
335 1538         3158 $prev_sib = $sib;
336             }
337 118 50       3651 $self->_messagef( 3, "Saving last %d nodes collected.", scalar( @$tmp_nodes ) ) if( $self->{debug} >= 3 );
338 118         216 push( @$tmp_ref, $tmp_nodes );
339 118 50       253 $self->_messagef( 3, "Found %d try-catch block(s) in initial PPI result.", scalar( @$tmp_ref ) ) if( $self->{debug} >= 3 );
340             # If we did find consecutive try-catch blocks, we add each of them after the nominal one and remove the nominal one after. The nominal one should be empty by then
341 118 100       275 if( scalar( @$tmp_ref ) > 1 )
342             {
343 2         3 my $last_obj = $this;
344 2         3 my $spaces = [];
345 2         4 foreach my $arr ( @$tmp_ref )
346             {
347 4 50       46 $self->_message( 3, "Adding statement block with ", scalar( @$arr ), " children after '$last_obj'" ) if( $self->{debug} >= 3 );
348             # 2021-06-05 (Jacques): Fixing issue No. 2:
349             # Find the last block that belongs to us
350 4 50       9 $self->_message( 4, "Checking first level objects collected." ) if( $self->{debug} >= 4 );
351 4         5 my $last_control = '';
352 4         6 my $last_block;
353 4         7 my $last = {};
354 4         5 foreach my $o ( @$arr )
355             {
356             # $self->_message( 4, "Found object '$o' of class '", $o->class, "' (", overload::StrVal( $o ), ")." );
357 57 100 100     261 if( $o->class eq 'PPI::Structure::Block' && $last_control )
    100          
358             {
359 8         48 $last->{block} = $o;
360 8         10 $last->{control} = $last_control;
361 8         17 $last_control = '';
362             }
363             elsif( $o->class eq 'PPI::Token::Word' )
364             {
365 11         73 my $ct = $o->content;
366 11 100 100     73 if( $ct eq 'try' || $ct eq 'catch' || $ct eq 'finally' )
      66        
367             {
368 8         14 $last_control = $o;
369             }
370             }
371             }
372             # $self->_message( 4, "Last control was '$last->{control}' and last block '$last->{block}' (", overload::StrVal( $last->{block} ), ")." );
373            
374             # Get the trailing insignificant elements at the end of the statement and move them out of the statement
375 4         26 my $insignificants = [];
376 4         11 while( scalar( @$arr ) > 0 )
377             {
378 25         946 my $o = $arr->[-1];
379             # $self->_message( 4, "Checking trailing object with class '", $o->class, "' and value '$o'" );
380             # 2021-06-05 (Jacques): We don't just look for the last block, because
381             # that was making a bad assumption that the last trailing block would be our
382             # try-catch block.
383             # Following issue No. 2 reported with a trailing anonymous subroutine,
384             # We remove everything up until our known last block that belongs to us.
385 25 100 100     49 last if( $o->class eq 'PPI::Structure::Block' && Scalar::Util::refaddr( $o ) eq Scalar::Util::refaddr( $last->{block} ) );
386 21         111 unshift( @$insignificants, pop( @$arr )->remove );
387             }
388 4 50       36 $self->_messagef( 3, "%d insignificant objects found.", scalar( @$insignificants ) ) if( $self->{debug} >= 3 );
389            
390 4         14 my $new_code = join( '', map( "$_", @$arr ) );
391             # $self->_message( 4, "New code is: '$new_code'" );
392             # 2021-06-05 (Jacques): It is unfortunately difficult to simply add a new PPI::Statement object
393             # Instead, we have PPI parse our new code and we grab what we need.
394 4         638 my $new_block = PPI::Document->new( \$new_code, readonly => 1 );
395             # $self->_message( 4, "New block code is: '$new_block'" );
396             # $self->_browse( $new_block );
397 4         18241 my $st = $new_block->{children}->[0]->remove;
398             # $self->_message( 4, "Statemnt now contains: '$st'" );
399            
400             # $self->_messagef( 3, "Adding the updated statement objects with %d children.", scalar( @$arr ) );
401 4         152 foreach my $o ( @$arr )
402             {
403             # We remove the object from its parent, now that it has become useless
404 36   50     935 my $old = $o->remove || die( "Unable to remove element '$o'\n" );
405             }
406 4         111 my $err = '';
407 4 0       11 $self->_messagef( 3, "Adding the statement object after last object '%s' of class '%s' with parent with class '%s'.", Scalar::Util::refaddr( $last_obj ), ( defined( $last_obj ) ? $last_obj->class : 'undefined class' ), ( defined( $last_obj ) ? $last_obj->parent->class : 'undefined parent class' ) ) if( $self->{debug} >= 3 );
    0          
    50          
408 4 50       11 $self->_message( 4, "In other word, adding:\n'$st'\nAFTER:\n'$last_obj'" ) if( $self->{debug} >= 4 );
409             # my $rc = $last_obj->insert_after( $st );
410 4         6 my $rc;
411 4 100       10 if( $last_obj->class eq 'PPI::Token::Whitespace' )
412             {
413 2         13 $rc = $last_obj->__insert_after( $st );
414             }
415             else
416             {
417 2         15 $rc = $last_obj->insert_after( $st );
418             }
419            
420 4 50       184 if( !defined( $rc ) )
    50          
421             {
422 0         0 $err = sprintf( 'Object to be added after last try-block statement must be a PPI::Element. Class provided is \"%s\".', $st->class );
423             }
424             elsif( !$rc )
425             {
426 0         0 $err = sprintf( "Object of class \"%s\" could not be added after object '%s' of class '%s' with parent '%s' with class '%s': '$last_obj'.", $st->class, Scalar::Util::refaddr( $last_obj ), $last_obj->class, Scalar::Util::refaddr( $last_obj->parent ), $last_obj->parent->class );
427             }
428             else
429             {
430 4         5 $last_obj = $st;
431 4 50       19 if( scalar( @$insignificants ) )
432             {
433 4 50       11 $self->_messagef( 4, "Adding %d trailing insignificant objects after last element of class '%s'", scalar( @$insignificants ), $last_obj->class ) if( $self->{debug} >= 4 );
434 4         9 foreach my $o ( @$insignificants )
435             {
436 21 50       49 $self->_messagef( 4, "Adding trailing insignificant object of class '%s' after last element of class '%s'", $o->class, $last_obj->class ) if( $self->{debug} >= 4 );
437             ## printf( STDERR "Inserting object '%s' (%s) of type '%s' after object '%s' (%s) of type %s who has parent '%s' of type '%s'\n", overload::StrVal( $o ), Scalar::Util::refaddr( $o ), ref( $o ), overload::StrVal( $last_obj), Scalar::Util::refaddr( $last_obj ), ref( $last_obj ), overload::StrVal( $last_obj->parent ), ref( $last_obj->parent ) );
438             CORE::eval
439 21         25 {
440             $rc = $last_obj->insert_after( $o ) ||
441             do
442 21   33     78 {
443             warn( "Failed to insert object of class '", $o->class, "' before last object of class '", $st->class, "'\n" ) if( $self->{debug} );
444             };
445             };
446 21 50       1190 if( $@ )
    50          
    50          
447             {
448 0 0       0 if( ref( $o ) )
449             {
450 0 0       0 warn( "Failed to insert object of class '", $o->class, "' before last object of class '", $st->class, "': $@\n" ) if( $self->{debug} );
451             }
452             else
453             {
454 0 0       0 warn( "Was expecting an object to insert before last object of class '", $st->class, "', but instead got '$o': $@\n" ) if( $self->{debug} );
455             }
456             }
457             elsif( !defined( $rc ) )
458             {
459 0 0       0 warn( sprintf( 'Object to be added after last try-block statement must be a PPI::Element. Class provided is \"%s\".', $o->class ) ) if( $self->{debug} );
460             }
461             elsif( !$rc )
462             {
463 0 0       0 warn( sprintf( "Object of class \"%s\" could not be added after object of class '%s': '$last_obj'.", $o->class, $last_obj->class ) ) if( $self->{debug} );
464             }
465             ## printf( STDERR "Object inserted '%s' (%s) of class '%s' now has parent '%s' (%s) of class '%s'\n", overload::StrVal( $o ), Scalar::Util::refaddr( $o ), ref( $o ), overload::StrVal( $o->parent ), Scalar::Util::refaddr( $o->parent ), ref( $o->parent ) );
466 21 50       45 $o->parent( $last_obj->parent ) if( !$o->parent );
467 21         101 $last_obj = $o;
468             }
469             }
470             }
471 4 50       16 die( $err ) if( length( $err ) );
472 4         21 push( @$alt_ref, $st );
473             }
474 2         49 my $parent = $this->parent;
475             ## Completely destroy it; it is now replaced by our updated code
476 2         14 $this->delete;
477             }
478             else
479             {
480 116         463 push( @$alt_ref, $this );
481             }
482             }
483 25 50       361 $self->_messagef( 3, "Results found increased from %d to %d results.", scalar( @$ref ), scalar( @$alt_ref ) ) if( $self->{debug} >= 3 );
484 25 100       116 @$ref = @$alt_ref if( scalar( @$alt_ref ) > scalar( @$ref ) );
485            
486             # $self->_message( 3, "Script code is now:\n'$elem'" );
487            
488 25         83 foreach my $this ( @$ref )
489             {
490 120 50       18131 $self->_browse( $this ) if( $self->{debug} >= 5 );
491             # $self->_message( 4, "\$this is of class '", $this->class, "' and its parent of class '", $this->parent->class, "'." );
492 120         590 my $element_before_try = $this->previous_sibling;
493             # $self->_message( 4, "Is \$element_before_try defined ? ", defined( $element_before_try ) ? 'Yes' : 'No', "(", overload::StrVal( $element_before_try ), ") -> '$element_before_try'" );
494 120         3032 my $try_block_ref = [];
495             # Contains the finally block reference
496 120         199 my $fin_block_ref = [];
497 120         183 my $nodes_to_replace = [];
498 120         195 my $catch_def = [];
499             # Replacement data
500 120         191 my $repl = [];
501 120         225 my $catch_repl = [];
502            
503             # There is a weird bug in PPI that I have searched but could not find
504             # If I don't attempt to stringify, I may end up with a PPI::Statement object that has no children as an array reference
505 120         326 my $ct = "$this";
506             # $self->_message( 3, "Checking sibling elements for '$ct'" );
507 120         40432 my( @block_children ) = $this->children;
508 120 100       840 next if( !scalar( @block_children ) );
509 115         222 my $prev_sib = $block_children[0];
510 115         294 push( @$nodes_to_replace, $prev_sib );
511 115         192 my( $inside_catch, $inside_finally );
512 115         245 my $temp = {};
513             # Buffer of nodes found in between blocks
514 115         196 my $buff = [];
515             # Temporary new line counter between try-catch block so we can reproduce it and ensure proper reporting of error line
516 115         206 my $nl_counter = 0;
517 115         207 my $sib;
518 115         356 while( $sib = $prev_sib->next_sibling )
519             {
520             # $self->_messagef( 3, "Try sibling at line %d with class '%s': '%s'", $sib->line_number, $sib->class, $sib->content );
521 1455 100 100     38940 if( !scalar( @$try_block_ref ) )
    100 100        
    100 100        
    100          
    100          
    100          
522             {
523             # $self->_message( 3, "\tWorking on the initial try block." );
524 296 100 66     732 if( $sib->class eq 'PPI::Structure::Block' &&
    100 66        
      100        
525             substr( "$sib", 0, 1 ) eq "\{" &&
526             substr( "$sib", -1, 1 ) eq "\}" )
527             {
528 113         31423 $temp->{block} = $sib;
529 113         239 push( @$try_block_ref, $temp );
530 113         247 $temp = {};
531 113 50       298 if( scalar( @$buff ) )
532             {
533 113         208 push( @$nodes_to_replace, @$buff );
534 113         258 $buff = [];
535             }
536 113         229 push( @$nodes_to_replace, $sib );
537             }
538             elsif( $sib->class eq 'PPI::Token::Whitespace' && $sib->content =~ /[\015\012]+/ )
539             {
540             ## $self->_messagef( 4, "\tTry -> Found open new line at line %d", $sib->line_number );
541 68         1262 $temp->{open_curly_nl}++;
542 68         168 push( @$buff, $sib );
543             }
544             ## We skip anything else until we find that try block
545             else
546             {
547 115         3010 push( @$buff, $sib );
548 115         185 $prev_sib = $sib;
549 115         345 next;
550             }
551             }
552             elsif( $sib->class eq 'PPI::Token::Word' && $sib->content eq 'catch' )
553             {
554 129         1143 $inside_catch++;
555 129 100       323 if( scalar( @$buff ) )
556             {
557 125         340 push( @$nodes_to_replace, @$buff );
558 125         249 $buff = [];
559             }
560 129         212 push( @$nodes_to_replace, $sib );
561             }
562             elsif( $inside_catch )
563             {
564             # $self->_message( 3, "\tWorking on a catch block." );
565             # This is the catch list as in catch( $e ) or catch( Exception $e )
566 448 100 66     2213 if( $sib->class eq 'PPI::Structure::List' )
    100          
    100          
567             {
568 96         460 $temp->{var} = $sib;
569 96         192 push( @$nodes_to_replace, $sib );
570             }
571             elsif( $sib->class eq 'PPI::Structure::Block' )
572             {
573 129         811 $temp->{block} = $sib;
574 129 100       272 if( scalar( @$catch_def ) )
575             {
576 24         53 $catch_def->[-1]->{close_curly_nl} = $nl_counter;
577             }
578             else
579             {
580 105         218 $try_block_ref->[-1]->{close_curly_nl} = $nl_counter;
581             }
582 129         216 $nl_counter = 0;
583 129         230 push( @$catch_def, $temp );
584 129         212 $temp = {};
585 129         197 $inside_catch = 0;
586 129         234 push( @$nodes_to_replace, $sib );
587             }
588             elsif( $sib->class eq 'PPI::Token::Whitespace' && $sib->content =~ /[\015\012]+/ )
589             {
590             # $self->_messagef( 4, "\tCatch -> Found open new line at line %d", $sib->line_number );
591 87         1416 $temp->{open_curly_nl}++;
592 87         198 push( @$nodes_to_replace, $sib );
593             }
594             else
595             {
596 136         1788 push( @$nodes_to_replace, $sib );
597             }
598             }
599             elsif( $sib->class eq 'PPI::Token::Word' && $sib->content eq 'finally' )
600             {
601 13         226 $inside_finally++;
602 13 50       38 if( scalar( @$buff ) )
603             {
604 13         33 push( @$nodes_to_replace, @$buff );
605 13         28 $buff = [];
606             }
607 13         21 push( @$nodes_to_replace, $sib );
608             }
609             elsif( $inside_finally )
610             {
611             ## $self->_message( 3, "\tWorking on a finally block." );
612             ## We could ignore it, but it is best to let the developer know in case he/she counts on it somehow
613 27 50 66     216 if( $sib->class eq 'PPI::Structure::List' )
    100          
    100          
614             {
615 0         0 die( sprintf( "the finally block does not accept any list parameters at line %d\n", $sib->line_number ) );
616             }
617             elsif( $sib->class eq 'PPI::Structure::Block' )
618             {
619 13         95 $temp->{block} = $sib;
620 13 50       61 if( scalar( @$fin_block_ref ) )
    100          
621             {
622 0         0 die( sprintf( "There can only be one finally block at line %d\n", $sib->line_number ) );
623             }
624             elsif( scalar( @$catch_def ) )
625             {
626 7         11 $catch_def->[-1]->{close_curly_nl} = $nl_counter;
627             }
628             else
629             {
630 6         24 $try_block_ref->[-1]->{close_curly_nl} = $nl_counter;
631             }
632 13         19 $nl_counter = 0;
633 13         22 push( @$fin_block_ref, $temp );
634 13         21 $temp = {};
635 13         18 $inside_finally = 0;
636 13         28 push( @$nodes_to_replace, $sib );
637             }
638             elsif( $sib->class eq 'PPI::Token::Whitespace' && $sib->content =~ /[\015\012]+/ )
639             {
640             ## $self->_messagef( 4, "\tFinally -> Found open new line at line %d", $sib->line_number );
641 1         36 $temp->{open_curly_nl}++;
642 1         2 push( @$nodes_to_replace, $sib );
643             }
644             else
645             {
646 13         221 push( @$nodes_to_replace, $sib );
647             }
648             }
649             # Check for new lines after closing blocks. The ones before, we can account for them in each section above
650             # We could have } catch {
651             # or
652             # }
653             # catch {
654             # etc.
655             # This could also be new lines following the last catch block
656             elsif( $sib->class eq 'PPI::Token::Whitespace' && $sib->content =~ /[\015\012]+/ )
657             {
658             # $self->_messagef( 4, "Between -> Found closing new line at line %d", $sib->line_number );
659 189         3511 $nl_counter++;
660 189         388 push( @$buff, $sib );
661             }
662             else
663             {
664 353         4539 push( @$buff, $sib );
665             }
666 1340         3086 $prev_sib = $sib;
667             }
668            
669 115 100       3746 my $has_catch_clause = scalar( @$catch_def ) > 0 ? 1 : 0;
670            
671             # Prepare the finally block, if any, and add it below at the appropriate place
672 115         222 my $fin_block = '';
673 115 100       301 if( scalar( @$fin_block_ref ) )
674             {
675 13         51 my $fin_def = $fin_block_ref->[0];
676 13         75 $self->_process_caller( finally => $fin_def->{block} );
677             ## my $finally_block = $fin_def->{block}->content;
678 13         103 my $finally_block = $self->_serialize( $fin_def->{block} );
679 13         162 $finally_block =~ s/^\{[[:blank:]]*|[[:blank:]]*\}$//gs;
680 13         45 $fin_block = <
681             CORE::local \$Nice::Try::FINALLY = Nice\::Try\::ScopeGuard->_new(sub __FINALLY_OPEN_NL__{ __BLOCK_PLACEHOLDER__ __FINALLY__CLOSE_NL__}, \@_);
682             EOT
683 13 50       102 $fin_block =~ s/\n/ /gs unless( $self->{debug_code} );
684 13         65 $fin_block =~ s/__BLOCK_PLACEHOLDER__/$finally_block/gs;
685 13 100       35 if( $fin_def->{open_curly_nl} )
686             {
687 1         4 $fin_block =~ s/__FINALLY_OPEN_NL__/"\n" x $fin_def->{open_curly_nl}/gex;
  1         6  
688             }
689             else
690             {
691 12         42 $fin_block =~ s/__FINALLY_OPEN_NL__//gs;
692             }
693 13 50       33 if( $fin_def->{close_curly_nl} )
694             {
695 0         0 $fin_block =~ s/__FINALLY__CLOSE_NL__/"\n" x $fin_def->{close_curly_nl}/gex;
  0         0  
696             }
697             else
698             {
699 13         56 $fin_block =~ s/__FINALLY__CLOSE_NL__//gs;
700             }
701             }
702              
703             # Found any try block at all?
704 115 100       302 if( scalar( @$try_block_ref ) )
705             {
706             # $self->_message( 3, "Original code to remove is:\n", join( '', @$nodes_to_replace ) );
707             # $self->_message( 3, "Try definition: ", $try_block_ref->[0]->{block}->content );
708             # $self->_messagef( 3, "%d catch clauses found", scalar( @$catch_def ) );
709 113         298 foreach my $c ( @$catch_def )
710             {
711             # $self->_message( 3, "Catch variable assignment: ", $c->{var} );
712             # $self->_message( 3, "Catch block: ", $c->{block} );
713             }
714 113         246 my $try_def = $try_block_ref->[0];
715             # $self->_messagef( 3, "Try new lines before block: %d, after block %d", $try_def->{open_curly_nl}, $try_def->{close_curly_nl} );
716            
717             # Checking for embedded try-catch
718             # $self->_message( 4, "Checking for embedded try-catch in ", $try_def->{block} );
719 113 100       1486 if( my $emb = $self->_parse( $try_def->{block} ) )
720             {
721 3         17 $try_def->{block} = $emb;
722             }
723            
724 113         424 $self->_process_loop_breaks( $try_def->{block} );
725 113         987 $self->_process_caller( try => $try_def->{block} );
726            
727             ## my $try_block = $try_def->{block}->content;
728 113         352 my $try_block = $self->_serialize( $try_def->{block} );
729 113         8468 $try_block =~ s/^\{[[:blank:]]*|[[:blank:]]*\}$//gs;
730            
731 113         527 my $try_sub = <
732             CORE::local \$Nice::Try::THREADED;
733             if( \$INC{'threads.pm'} && !CORE::exists( \$INC{'forks.pm'} ) )
734             {
735             \$Nice::Try::THREADED = threads->tid;
736             }
737             CORE::local \$Nice::Try::WANT;
738             CORE::local ( \$Nice::Try::EXCEPTION, \$Nice::Try::DIED, \@Nice::Try::RETVAL, \@Nice::Try::VOID );
739             CORE::local \$Nice::Try::WANTARRAY = CORE::wantarray;
740             CORE::local \$Nice::Try::TRY = CORE::sub
741             {
742             \@Nice::Try::LAST_VAL = CORE::do __TRY_OPEN_NL__{ __BLOCK_PLACEHOLDER__ };__TRY__CLOSE_NL__
743             CORE::return( \@Nice::Try::LAST_VAL ) if( !CORE::defined( \$Nice::Try::WANTARRAY ) && CORE::scalar( \@Nice::Try::LAST_VAL ) );
744             CORE::return( \$Nice::Try::VOID[0] = \$Nice::Try::SENTINEL );
745             };
746             __FINALLY_BLOCK__ CORE::local \$Nice::Try::HAS_CATCH = $has_catch_clause;
747             EOT
748 113 50 33     827 if( !$self->{is_tied} && !$self->{dont_want} && !$self->{is_overloaded} )
      33        
749             {
750 113         1243 $try_sub .= <
751             CORE::local \$Nice::Try::NOOP = sub
752             {
753             my \$ref = CORE::shift( \@_ );
754             CORE::return(sub{ CORE::return( \$ref ) });
755             };
756             if( CORE::defined( \$Nice::Try::WANTARRAY ) && !\$Nice::Try::THREADED && !( !CORE::length( [CORE::caller]->[1] ) && [CORE::caller(1)]->[3] eq '(eval)' ) )
757             {
758             CORE::eval "\\\$Nice::Try::WANT = Want::want( 'LIST' )
759             ? 'LIST'
760             : Want::want( 'HASH' )
761             ? 'HASH'
762             : Want::want( 'ARRAY' )
763             ? 'ARRAY'
764             : Want::want( 'OBJECT' )
765             ? 'OBJECT'
766             : Want::want( 'CODE' )
767             ? 'CODE'
768             : Want::want( 'REFSCALAR' )
769             ? 'REFSCALAR'
770             : Want::want( 'BOOLEAN' )
771             ? 'BOOLEAN'
772             : Want::want( 'GLOB' )
773             ? 'GLOB'
774             : Want::want( 'SCALAR' )
775             ? 'SCALAR'
776             : Want::want( 'VOID' )
777             ? 'VOID'
778             : '';";
779             undef( \$Nice::Try::WANT ) if( \$\@ );
780             }
781             EOT
782             }
783 113         626 $try_sub .= <
784             {
785             CORE::local \$\@;
786             CORE::eval
787             {
788             if( CORE::defined( \$Nice::Try::WANT ) && CORE::length( \$Nice::Try::WANT ) )
789             {
790             if( \$Nice::Try::WANT eq 'OBJECT' )
791             {
792             \$Nice::Try::RETVAL[0] = Nice::Try::ObjectContext->new( &\$Nice::Try::TRY )->callback();
793             }
794             elsif( \$Nice::Try::WANT eq 'CODE' )
795             {
796             \$Nice::Try::RETVAL[0] = \$Nice::Try::NOOP->( &\$Nice::Try::TRY )->();
797             }
798             elsif( \$Nice::Try::WANT eq 'HASH' )
799             {
800             \@Nice::Try::RETVAL = \%{ &\$Nice::Try::TRY };
801             }
802             elsif( \$Nice::Try::WANT eq 'ARRAY' )
803             {
804             \@Nice::Try::RETVAL = \@{ &\$Nice::Try::TRY };
805             }
806             elsif( \$Nice::Try::WANT eq 'REFSCALAR' )
807             {
808             \$Nice::Try::RETVAL[0] = \${&\$Nice::Try::TRY};
809             }
810             elsif( \$Nice::Try::WANT eq 'GLOB' )
811             {
812             \$Nice::Try::RETVAL[0] = \*{ &\$Nice::Try::TRY };
813             }
814             elsif( \$Nice::Try::WANT eq 'LIST' )
815             {
816             \@Nice::Try::RETVAL = &\$Nice::Try::TRY;
817             }
818             elsif( \$Nice::Try::WANT eq 'BOOLEAN' )
819             {
820             \$Nice::Try::RETVAL[0] = &\$Nice::Try::TRY ? 1 : 0;
821             \$Nice::Try::RETVAL[0] = \$Nice::Try::VOID[0] if( scalar( \@Nice::Try::VOID ) );
822             }
823             elsif( \$Nice::Try::WANT eq 'VOID' )
824             {
825             \$Nice::Try::VOID[0] = &\$Nice::Try::TRY;
826             }
827             elsif( \$Nice::Try::WANT eq 'SCALAR' )
828             {
829             \$Nice::Try::RETVAL[0] = &\$Nice::Try::TRY;
830             }
831             }
832             else
833             {
834             if( \$Nice::Try::WANTARRAY )
835             {
836             \@Nice::Try::RETVAL = &\$Nice::Try::TRY;
837             }
838             elsif( defined( \$Nice::Try::WANTARRAY ) )
839             {
840             \$Nice::Try::RETVAL[0] = &\$Nice::Try::TRY;
841             }
842             else
843             {
844             &\$Nice::Try::TRY;
845             \$Nice::Try::RETVAL[0] = \$Nice::Try::LAST_VAL if( CORE::defined( \$Nice::Try::LAST_VAL ) );
846             }
847             }
848             };
849             \$Nice::Try::DIED = CORE::length( \$\@ ) ? 1 : 0;
850             \$\@ =~ s/[\\015\\012]+\$//g unless( Scalar::Util::blessed( \$\@ ) );
851             \$Nice::Try::EXCEPTION = \$\@;
852             };
853              
854             EOT
855 113 50       4111 $try_sub =~ s/\n/ /gs unless( $self->{debug_code} );
856 113         1062 $try_sub =~ s/__BLOCK_PLACEHOLDER__/$try_block/gs;
857 113 100       394 if( $try_def->{open_curly_nl} )
858             {
859 68         313 $try_sub =~ s/__TRY_OPEN_NL__/"\n" x $try_def->{open_curly_nl}/gex;
  68         560  
860             }
861             else
862             {
863 45         452 $try_sub =~ s/__TRY_OPEN_NL__//gs;
864             }
865 113 100       369 if( $try_def->{close_curly_nl} )
866             {
867 103         511 $try_sub =~ s/__TRY__CLOSE_NL__/"\n" x $try_def->{close_curly_nl}/gex;
  103         743  
868             }
869             else
870             {
871 10         91 $try_sub =~ s/__TRY__CLOSE_NL__//gs;
872             }
873            
874             # Add the final block if there is no catch block, otherwise the final block comes at the end below
875 113 100       317 if( !$has_catch_clause )
876             {
877 8         67 $try_sub =~ s/__FINALLY_BLOCK__/$fin_block/gs;
878             }
879             # If it should not be here, remove the placeholder
880             else
881             {
882 105         833 $try_sub =~ s/__FINALLY_BLOCK__//gs;
883             }
884 113         501 push( @$repl, $try_sub );
885             }
886             else
887             {
888             # $self->_message( 3, "** No try block found!!" );
889 2         9 next;
890             }
891            
892 113         215 my $if_start = <
893             if( \$Nice::Try::DIED )
894             {
895             if( \$Nice::Try::HAS_CATCH )
896             {
897             EOT
898 113 50       627 $if_start =~ s/\n/ /gs unless( $self->{debug_code} );
899 113         262 push( @$catch_repl, $if_start );
900 113 100       239 if( scalar( @$catch_def ) )
901             {
902             # $self->_messagef( 3, "Found %d catch blocks", scalar( @$catch_def ) );
903 105         172 my $total_catch = scalar( @$catch_def );
904             # To count how many times we have else's – obviously we should not have more than 1
905 105         193 my $else = 0;
906 105         297 for( my $i = 0; $i < $total_catch; $i++ )
907             {
908 129         250 my $cdef = $catch_def->[$i];
909             # $self->_messagef( 3, "Catch No ${i} new lines before block: %d, after block %d", $cdef->{open_curly_nl}, $cdef->{close_curly_nl} );
910             # Checking for embedded try-catch
911 129 100       443 if( my $emb = $self->_parse( $cdef->{block} ) )
912             {
913 1         3 $cdef->{block} = $emb;
914             }
915            
916 129 100       425 if( $cdef->{var} )
917             {
918 96         413 $cdef->{var}->prune( 'PPI::Token::Comment' );
919 96         35515 $cdef->{var}->prune( 'PPI::Token::Pod' );
920 96         32112 $self->_messagef( 3, "Catch assignment is: '%s'", $cdef->{var}->content );
921             # my $str = $cdef->{var}->content;
922 96         275 my $str = $self->_serialize( $cdef->{var} );
923 96         743 $str =~ s/^\([[:blank:]\h\v]*|[[:blank:]]*\)$//g;
924             # My::Exception $e
925 96 100       626 if( $str =~ /^(\S+)[[:blank:]\h\v]+(\$\S+)$/ )
    100          
    100          
    100          
    100          
926             {
927 15         95 @$cdef{qw( class var )} = ( $1, $2 );
928             }
929             elsif( $str =~ /^(\S+)[[:blank:]\h\v]+(\$\S+)[[:blank:]\h\v]+where[[:blank:]\h\v]+\{(.*?)\}$/ )
930             {
931 4         32 @$cdef{qw( class var where )} = ( $1, $2, $3 );
932             }
933             elsif( $str =~ /^(\$\S+)[[:blank:]\h\v]+where[[:blank:]\h\v]+\{(.*?)\}$/ )
934             {
935 1         19 @$cdef{qw( var where )} = ( $1, $2 );
936             }
937             elsif( $str =~ /^(\$\S+)[[:blank:]\h\v]+isa[[:blank:]\h\v]+(\S+)(?:[[:blank:]\h\v]+where[[:blank:]\h\v]+\{(.*?)\})?$/ )
938             {
939 9         59 @$cdef{qw( var class where )} = ( $1, $2, $3 );
940             }
941             elsif( $str =~ /^(?\$\S+)[[:blank:]\h\v]+isa[[:blank:]\h\v]*\([[:blank:]\h\v]*(?["'])?(?[^[:blank:]\h\v\'\"\)]+)\k{quote}[[:blank:]\h\v]*\)(?:[[:blank:]\h\v]+where[[:blank:]\h\v]+\{(?.*?)\})?$/ )
942             {
943 24     24   12384 @$cdef{qw( var class where )} = ( $+{var}, $+{class}, $+{where} );
  24         9524  
  24         40757  
  2         38  
944             }
945             else
946             {
947 65         164 $cdef->{var} = $str;
948             }
949             }
950             else
951             {
952             # $self->_message( 3, "No Catch assignment found" );
953             }
954            
955 129 50       459 if( $cdef->{block} )
956             {
957             # $self->_messagef( 3, "Catch block is:\n%s", $cdef->{block}->content );
958             }
959             else
960             {
961             # $self->_message( 3, "No catch block found!" );
962 0         0 next;
963             }
964 129         206 my $cond;
965 129 100       302 if( $i == 0 )
    100          
966             {
967 105         177 $cond = 'if';
968             }
969             elsif( $i == ( $total_catch - 1 ) )
970             {
971             $cond = $total_catch == 1
972             ? 'if'
973             : $cdef->{class}
974 11 100       46 ? 'elsif'
    50          
975             : 'else';
976             }
977             else
978             {
979 13         25 $cond = 'elsif';
980             }
981             # $self->_message( 3, "\$i = $i, \$total_catch = $total_catch and cond = '$cond'" );
982             # my $block = $cdef->{block}->content;
983 129         383 $self->_process_loop_breaks( $cdef->{block} );
984 129         1716 $self->_process_caller( catch => $cdef->{block} );
985 129         381 my $block = $self->_serialize( $cdef->{block} );
986 129         3195 $block =~ s/^\{[[:blank:]]*|[[:blank:]]*\}$//gs;
987 129         290 my $catch_section = '';
988 129         448 my $catch_code = <
989             CORE::local \$Nice::Try::CATCH = CORE::sub
990             {
991             \@Nice::Try::LAST_VAL = CORE::do __CATCH_OPEN_NL__{ __BLOCK_PLACEHOLDER__ }; __CATCH__CLOSE_NL__
992             CORE::return( \@Nice::Try::LAST_VAL ) if( !CORE::defined( \$Nice::Try::WANTARRAY ) && CORE::scalar( \@Nice::Try::LAST_VAL ) );
993             CORE::return \$Nice::Try::SENTINEL;
994             };
995            
996             if( CORE::defined( \$Nice::Try::WANT ) && CORE::length( \$Nice::Try::WANT ) )
997             {
998             if( \$Nice::Try::WANT eq 'OBJECT' )
999             {
1000             \$Nice::Try::RETVAL[0] = Nice::Try::ObjectContext->new( \&\$Nice::Try::CATCH )->callback();
1001             }
1002             elsif( \$Nice::Try::WANT eq 'CODE' )
1003             {
1004             \$Nice::Try::RETVAL[0] = \$Nice::Try::NOOP->( \&\$Nice::Try::CATCH )->();
1005             }
1006             elsif( \$Nice::Try::WANT eq 'HASH' )
1007             {
1008             \@Nice::Try::RETVAL = \%{ \&\$Nice::Try::CATCH };
1009             }
1010             elsif( \$Nice::Try::WANT eq 'ARRAY' )
1011             {
1012             \@Nice::Try::RETVAL = \@{ \&\$Nice::Try::CATCH };
1013             }
1014             elsif( \$Nice::Try::WANT eq 'REFSCALAR' )
1015             {
1016             \$Nice::Try::RETVAL[0] = \${\&\$Nice::Try::CATCH};
1017             }
1018             elsif( \$Nice::Try::WANT eq 'GLOB' )
1019             {
1020             \$Nice::Try::RETVAL[0] = \*{ \&\$Nice::Try::CATCH };
1021             }
1022             elsif( \$Nice::Try::WANT eq 'LIST' )
1023             {
1024             \@Nice::Try::RETVAL = \&\$Nice::Try::CATCH;
1025             }
1026             elsif( \$Nice::Try::WANT eq 'BOOLEAN' )
1027             {
1028             my \$this = \&\$Nice::Try::CATCH ? 1 : 0;
1029             \$Nice::Try::RETVAL[0] = \$Nice::Try::VOID[0] if( scalar( \@Nice::Try::VOID ) );
1030             }
1031             elsif( \$Nice::Try::WANT eq 'VOID' )
1032             {
1033             \$Nice::Try::VOID[0] = \&\$Nice::Try::CATCH;
1034             }
1035             elsif( \$Nice::Try::WANT eq 'SCALAR' )
1036             {
1037             \$Nice::Try::RETVAL[0] = \&\$Nice::Try::CATCH;
1038             }
1039             }
1040             else
1041             {
1042             if( \$Nice::Try::WANTARRAY )
1043             {
1044             \@Nice::Try::RETVAL = \&\$Nice::Try::CATCH;
1045             }
1046             elsif( defined( \$Nice::Try::WANTARRAY ) )
1047             {
1048             \$Nice::Try::RETVAL[0] = \&\$Nice::Try::CATCH;
1049             }
1050             else
1051             {
1052             \&\$Nice::Try::CATCH;
1053             }
1054             }
1055             EOT
1056 129 100       339 if( $cdef->{var} )
1057             {
1058 96         183 my $ex_var = $cdef->{var};
1059 96 100 100     411 if( $cdef->{class} && $cdef->{where} )
    100          
    100          
1060             {
1061 12         27 my $ex_class = $cdef->{class};
1062 12         28 my $eval = "q{CORE::local \$_ = \$Nice::Try::EXCEPTION; my $ex_var = \$Nice::Try::EXCEPTION; CORE::local \$\@ = \$Nice::Try::EXCEPTION; $cdef->{where}}";
1063 12         112 $catch_section = <
1064             ${cond}( Scalar::Util::blessed( \$Nice::Try::EXCEPTION ) && \$Nice::Try::EXCEPTION->isa( '$ex_class' ) && CORE::eval( $eval ) )
1065             {
1066             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1067             my $ex_var = \$Nice::Try::EXCEPTION;
1068             $catch_code
1069             }
1070             EOT
1071             }
1072             elsif( $cdef->{class} )
1073             {
1074 18         47 my $ex_class = $cdef->{class};
1075             # Tilmann Haeberle (TH) 2021-03-25: Fix: properly test for exception class inheritance via ->isa
1076 18         195 $catch_section = <
1077             ${cond}( Scalar::Util::blessed( \$Nice::Try::EXCEPTION ) && \$Nice::Try::EXCEPTION->isa( '$ex_class' ) )
1078             {
1079             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1080             my $ex_var = \$Nice::Try::EXCEPTION;
1081             $catch_code
1082             }
1083             EOT
1084             }
1085             elsif( $cdef->{where} )
1086             {
1087 1         6 my $eval = "q{CORE::local \$_ = \$Nice::Try::EXCEPTION; my $ex_var = \$Nice::Try::EXCEPTION; CORE::local \$\@ = \$Nice::Try::EXCEPTION; $cdef->{where}}";
1088 1         12 $catch_section = <
1089             ${cond}( CORE::eval( $eval ) )
1090             {
1091             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1092             my $ex_var = \$Nice::Try::EXCEPTION;
1093             $catch_code
1094             }
1095             EOT
1096             }
1097             # No class, just variable assignment like $e or something
1098             else
1099             {
1100             # $self->_message( 3, "Called here for fallback for element No $i" );
1101 65 50       181 if( ++$else > 1 )
1102             {
1103             # CORE::warn( "Cannot have more than one falllback catch clause for block: ", $cdef->{block}->content, "\n" ) if( warnings::enabled );
1104 0 0       0 CORE::warn( "Cannot have more than one falllback catch clause for block: ", $self->_serialize( $cdef->{block} ), "\n" ) if( warnings::enabled );
1105             # Skip, not die. Not fatal, just ignored
1106 0         0 next;
1107             }
1108 65 100 66     254 $cond = "${cond}( 1 )" if( $cond eq 'if' || $cond eq 'elsif' );
1109             # push( @$catch_repl, <
1110 65         657 $catch_section = <
1111             ${cond}
1112             {
1113             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1114             my $ex_var = \$Nice::Try::EXCEPTION;
1115             $catch_code
1116             }
1117             EOT
1118             }
1119             }
1120             # No variable assignment like $e
1121             else
1122             {
1123 33 50 33     146 $cond = "${cond}( 1 )" if( $cond eq 'if' || $cond eq 'elsif' );
1124 33         278 $catch_section = <
1125             ${cond}
1126             {
1127             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1128             $catch_code
1129             }
1130             EOT
1131             }
1132 129 50       3297 $catch_section =~ s/\n/ /gs unless( $self->{debug_code} );
1133 129         992 $catch_section =~ s/__BLOCK_PLACEHOLDER__/$block/gs;
1134 129 100       367 if( $cdef->{open_curly_nl} )
1135             {
1136 87         354 $catch_section =~ s/__CATCH_OPEN_NL__/"\n" x $cdef->{open_curly_nl}/gex;
  87         543  
1137             }
1138             else
1139             {
1140 42         277 $catch_section =~ s/__CATCH_OPEN_NL__//gs;
1141             }
1142 129 100       368 if( $cdef->{close_curly_nl} )
1143             {
1144 27         130 $catch_section =~ s/__CATCH__CLOSE_NL__/"\n" x $cdef->{close_curly_nl}/gex;
  27         144  
1145             }
1146             else
1147             {
1148 102         680 $catch_section =~ s/__CATCH__CLOSE_NL__//gs;
1149             }
1150 129         614 push( @$catch_repl, $catch_section );
1151             }
1152             # End catch loop
1153             # Tilmann Haeberle (TH) 2021-03-25: Fix: put an else at the end to avoid 'fall_through' issue unless an else exists already
1154 105         176 my $if_end;
1155 105 100       260 if( $else )
1156             {
1157 65         108 $if_end = <
1158             }
1159             EOT
1160             }
1161             else
1162             {
1163 40         84 $if_end = <
1164             else
1165             {
1166             die( \$Nice::Try::EXCEPTION );
1167             }
1168             }
1169             EOT
1170             }
1171 105 50       469 $if_end =~ s/\n/ /g unless( $self->{debug_code} );
1172 105         244 push( @$catch_repl, $if_end );
1173             }
1174             # No catch clause
1175             else
1176             {
1177             # If the try-catch block is called inside an eval, propagate the exception
1178             # Otherwise, we just make the $@ available
1179 8         21 my $catch_else = <
1180             }
1181             else
1182             {
1183             if( CORE::defined( (CORE::caller(0))[3] ) && (CORE::caller(0))[3] eq '(eval)' )
1184             {
1185             CORE::die( \$Nice::Try::EXCEPTION );
1186             }
1187             else
1188             {
1189             \$\@ = \$Nice::Try::EXCEPTION;
1190             }
1191             }
1192             EOT
1193 8 50       53 $catch_else =~ s/\n/ /g unless( $self->{debug_code} );
1194 8         21 push( @$catch_repl, $catch_else );
1195             }
1196            
1197             # Add
1198 113 50       937 my $catch_res = scalar( @$catch_repl ) ? join( '', @$catch_repl ) : '';
1199 113 50       362 push( @$repl, $catch_res ) if( $catch_res );
1200             # Closing the If DIED condition
1201 113         215 push( @$repl, "\};" );
1202              
1203             # If there is a catch clause, we put the final block here, if any
1204 113 100 100     1959 if( $has_catch_clause && CORE::length( $fin_block ) )
1205             {
1206 7         11 push( @$repl, $fin_block );
1207             }
1208            
1209 113         385 my $last_return_block = <
1210             if( ( CORE::defined( \$Nice::Try::WANTARRAY ) || ( defined( \$Nice::Try::BREAK ) && \$Nice::Try::BREAK eq 'return' ) ) and
1211             (
1212             !Scalar::Util::blessed( \$Nice::Try::RETVAL[0] ) or
1213             ( Scalar::Util::blessed( \$Nice::Try::RETVAL[0] ) && !\$Nice::Try::RETVAL[0]->isa( 'Nice::Try::SENTINEL' ) )
1214             ) )
1215             {
1216             if( !CORE::defined( \$Nice::Try::BREAK ) || \$Nice::Try::BREAK eq 'return' )
1217             {
1218             if( CORE::defined( \$Nice::Try::WANT ) && CORE::length( \$Nice::Try::WANT ) )
1219             {
1220             if( \$Nice::Try::WANT eq 'LIST' )
1221             {
1222             CORE::return( \@Nice::Try::RETVAL );
1223             }
1224             elsif( \$Nice::Try::WANT eq 'VOID' )
1225             {
1226             if( CORE::defined( \$Nice::Try::RETVAL[0] ) && \$Nice::Try::RETVAL[0] eq '__NEXT__' )
1227             {
1228             \$Nice::Try::BREAK = 'next';
1229             }
1230             elsif( CORE::defined( \$Nice::Try::RETVAL[0] ) && \$Nice::Try::RETVAL[0] eq '__LAST__' )
1231             {
1232             \$Nice::Try::BREAK = 'last';
1233             }
1234             elsif( CORE::defined( \$Nice::Try::RETVAL[0] ) && \$Nice::Try::RETVAL[0] eq '__REDO__' )
1235             {
1236             \$Nice::Try::BREAK = 'redo';
1237             }
1238             elsif( defined( \$Nice::Try::BREAK ) && \$Nice::Try::BREAK eq 'return' )
1239             {
1240             CORE::return( \$Nice::Try::RETVAL[0] );
1241             }
1242             }
1243             elsif( \$Nice::Try::WANT eq 'OBJECT' )
1244             {
1245             CORE::return( \$Nice::Try::RETVAL[0] );
1246             }
1247             elsif( \$Nice::Try::WANT eq 'REFSCALAR' )
1248             {
1249             CORE::return( \\\$Nice::Try::RETVAL[0] );
1250             }
1251             elsif( \$Nice::Try::WANT eq 'SCALAR' )
1252             {
1253             CORE::return( \$Nice::Try::RETVAL[0] );
1254             }
1255             elsif( \$Nice::Try::WANT eq 'BOOLEAN' )
1256             {
1257             CORE::return( \$Nice::Try::RETVAL[0] );
1258             }
1259             elsif( \$Nice::Try::WANT eq 'CODE' )
1260             {
1261             CORE::return( \$Nice::Try::RETVAL[0] );
1262             }
1263             elsif( \$Nice::Try::WANT eq 'HASH' )
1264             {
1265             CORE::return( { \@Nice::Try::RETVAL } );
1266             }
1267             elsif( \$Nice::Try::WANT eq 'ARRAY' )
1268             {
1269             CORE::return( \\\@Nice::Try::RETVAL );
1270             }
1271             elsif( \$Nice::Try::WANT eq 'GLOB' )
1272             {
1273             CORE::return( \$Nice::Try::RETVAL[0] );
1274             }
1275             }
1276             else
1277             {
1278             CORE::return( \$Nice::Try::WANTARRAY ? \@Nice::Try::RETVAL : \$Nice::Try::RETVAL[0] );
1279             }
1280             }
1281             }
1282             EOT
1283 113 50       2465 $last_return_block =~ s/\n/ /gs unless( $self->{debug_code} );
1284 113         538 push( @$repl, $last_return_block );
1285 113         2074 my $try_catch_code = join( '', @$repl );
1286             # my $token = PPI::Token->new( "; \{ $try_catch_code \}" ) || die( "Unable to create token" );
1287             # XXX 2021-05-11 (Jacques): Need to remove blocks so that next or last statements can be effective.
1288 113         322 my $envelop = <
1289             ; CORE::local( \$Nice::Try::BREAK, \@Nice::Try::LAST_VAL );
1290             \{
1291             __TRY_CATCH_CODE__
1292             \}
1293             if( CORE::defined( \$Nice::Try::BREAK ) )
1294             {
1295             if( \$Nice::Try::BREAK eq 'next' )
1296             {
1297             CORE::next;
1298             }
1299             elsif( \$Nice::Try::BREAK eq 'last' )
1300             {
1301             CORE::last;
1302             }
1303             elsif( \$Nice::Try::BREAK eq 'redo' )
1304             {
1305             CORE::redo;
1306             }
1307             }
1308             no warnings 'void';
1309             CORE::scalar( \@Nice::Try::LAST_VAL ) > 1 ? \@Nice::Try::LAST_VAL : \$Nice::Try::LAST_VAL[0];
1310             EOT
1311 113 50       1162 $envelop =~ s/\n/ /gs unless( $self->{debug_code} );
1312 113         1903 $envelop =~ s/__TRY_CATCH_CODE__/$try_catch_code/;
1313 113   50     627 my $token = PPI::Token->new( $envelop ) || die( "Unable to create token" );
1314 113         4564 $token->set_class( 'Structure' );
1315             # $self->_messagef( 3, "Token is '$token' and of class '%s' and inherit from PPI::Token? %s", $token->class, ($token->isa( 'PPI::Token' ) ? 'yes' : 'no' ) );
1316 113   50     2154 my $struct = PPI::Structure->new( $token ) || die( "Unable to create PPI::Structure element" );
1317             # $self->_message( 3, "Resulting try-catch block is:\n'$token'" );
1318 113         4312 my $orig_try_catch_block = join( '', @$nodes_to_replace );
1319             # $self->_message( 3, "Original try-catch block is:\n'$orig_try_catch_block'" );
1320             # $self->_messagef( 3, "Element before our try-catch block is of class %s with value '%s'", $element_before_try->class, $element_before_try->content );
1321 113         33607 my $rc;
1322 113 50       451 if( !( $rc = $element_before_try->insert_after( $token ) ) )
1323             {
1324             # $self->_message( 3, "Return code is defined? ", CORE::defined( $rc ) ? 'yes' : 'no', " and is it a PPI::Element object? ", $token->isa( 'PPI::Element' ) ? 'yes' : 'no' );
1325 0         0 $self->_error( "Failed to add replacement code of class '", $token->class, "' after '$element_before_try'" );
1326 0         0 next;
1327             }
1328 113 0       6693 $self->_message( 3, "Return code is defined? ", defined( $rc ) ? "yes" : "no" ) if( $self->{debug} >= 3 );
    50          
1329            
1330 113         373 for( my $k = 0; $k < scalar( @$nodes_to_replace ); $k++ )
1331             {
1332 1293         80812 my $e = $nodes_to_replace->[$k];
1333             ## $self->_messagef( 4, "[$k] Removing node: $e" );
1334 1293 50       2664 $e->delete || warn( "Could not remove node No $k: '$e'\n" );
1335             }
1336             }
1337             # End foreach catch found
1338            
1339             # $self->_message( 3, "\n\nResulting code is\n", $elem->content );
1340 25         8450 return( $elem );
1341             }
1342              
1343             # .Element: [11] class PPI::Token::Word, value caller
1344             # .Element: [11] class PPI::Structure::List, value (1)
1345             #
1346             # ..Element: [12] class PPI::Token::Word, value caller
1347             # ..Element: [12] class PPI::Token::Structure, value ;
1348              
1349             sub _process_caller
1350             {
1351 1428     1428   2248 my $self = shift( @_ );
1352 1428         1909 my $where = shift( @_ );
1353 1428   50     3573 my $elem = shift( @_ ) || return( '' );
1354 24     24   285 no warnings 'uninitialized';
  24         88  
  24         6058  
1355 1428 100       2776 return( $elem ) if( !$elem->children );
1356 1419         7406 foreach my $e ( $elem->elements )
1357             {
1358 7077   50     19600 my $content = $e->content // '';
1359             # $self->_messagef( 6, "Checking element: [%d] class %s with %d children and value '%s'\n", $e->line_number, $e->class, ( $e->can('elements') ? scalar( $e->elements ) : 0 ), $content );
1360 7077         74932 my $class = $e->class;
1361 7077 100 100     24233 if( $class eq 'PPI::Token::Word' && $content =~ /^(?:CORE\::)?(?:GLOBAL\::)?caller$/ )
1362             {
1363             # $self->_message( 4, "Found caller, replacing with ", 'Nice::Try::caller_' . $where );
1364 5         22 $e->set_content( 'Nice::Try::caller_' . $where );
1365             }
1366            
1367 7077 100 100     19095 if( $e->can('elements') && $e->elements )
1368             {
1369 1173         9568 $self->_process_caller( $where => $e );
1370             }
1371             }
1372             # $self->_message( 5, "Element now is: '$elem'" );
1373             # $self->_browse( $elem );
1374 1419         2573 return( $elem );
1375             }
1376              
1377             sub _process_loop_breaks
1378             {
1379 1223     1223   1857 my $self = shift( @_ );
1380 1223   50     3138 my $elem = shift( @_ ) || return( '' );
1381 24     24   233 no warnings 'uninitialized';
  24         58  
  24         23550  
1382 1223 100       2936 return( $elem ) if( !$elem->children );
1383 1215 50       6594 $self->_message( 5, "Checking ", scalar( $elem->elements ), " elements for '$elem'" ) if( $self->{debug} >= 5 );
1384 1215         2422 foreach my $e ( $elem->elements )
1385             {
1386 5998   50     19910 my $content = $e->content // '';
1387 5998 0       68118 $self->_messagef( 6, "Checking element: [%d] class %s with %d children and value '%s'\n", $e->line_number, $e->class, ( $e->can('elements') ? scalar( $e->elements ) : 0 ), $content ) if( $self->{debug} >= 6 );
    50          
1388 5998         10519 my $class = $e->class;
1389             # We found a for, foreach or while loops and we skip, because if there are any break words (next, last, redo) inside, it is not our problem.
1390 5998 50 66     36503 if( $class eq 'PPI::Structure::For' ||
    50 66        
    100 33        
      66        
1391             ( $class eq 'PPI::Statement::Compound' &&
1392             CORE::defined( $e->first_element->content ) &&
1393             $e->first_element->content =~ /^(for|foreach|while)$/ ) )
1394             {
1395             # $self->_message( 6, "Skipping it. Its first word was '", $e->first_element->content, "'" );
1396 0         0 next;
1397             }
1398             elsif( $class eq 'PPI::Token::Word' && $content =~ /^(?:CORE\::)?(?:GLOBAL\::)?(next|last|redo)$/ )
1399             {
1400 0 0       0 $self->_message( 5, "Found loop keyword '$content'." ) if( $self->{debug} >= 5 );
1401             # $e->set_content( qq{CORE::return( '__} . uc( $1 ) . qq{__' )} );
1402             # $e->set_content( q{$Nice::Try::BREAK='__} . uc( $1 ) . qq{__' ); return;} );
1403 0         0 my $break_code = q{$Nice::Try::BREAK='} . $1 . qq{', return;};
1404 0         0 my $break_doc = PPI::Document->new( \$break_code, readonly => 1 );
1405 0         0 my $new_elem = $break_doc->first_element;
1406             # $self->_browse( $new_elem );
1407 0         0 $new_elem->remove;
1408 0 0   0   0 $self->_message( 5, "New element is object '", sub{ overload::StrVal( $new_elem ) }, "' -> $new_elem" ) if( $self->{debug} >= 5 );
  0         0  
1409             # Not yet implemented as of 2021-05-11 dixit PPI, so we use a hack to make it available anyhow
1410 0         0 $e->replace( $new_elem );
1411 0 0       0 $self->_message( 5, "Loop keyword now replaced with '$e'." ) if( $self->{debug} >= 5 );
1412             }
1413             elsif( $class eq 'PPI::Statement::Break' )
1414             {
1415 82         315 my $words = $e->find( 'PPI::Token::Word' );
1416 82 50       27430 $self->_messagef( 5, "Found %d word elements inside break element.", scalar( @$words ) ) if( $self->{debug} >= 5 );
1417 82 50 50     338 my $word1 = ( scalar( @$words ) ? $words->[0]->content // '' : '' );
1418 82 100 50     505 my $word2 = ( scalar( @$words ) > 1 ? $words->[1]->content // '' : '' );
1419 82 50       252 $self->_message( 5, "Word 1 -> ", $word1 ) if( $self->{debug} >= 5 );
1420 82 50 33     250 $self->_message( 5, "Word 2 -> ", $word2 ) if( $self->{debug} >= 5 && scalar( @$words ) > 1 );
1421             # $self->_browse( $e );
1422             # If we found a break word without a label, i.e. next, last, redo,
1423             # we replace it with a special return statement
1424 82 50 100     793 if( (
      66        
      100        
1425             scalar( @$words ) == 1 ||
1426             ( scalar( @$words ) > 1 && $word2 =~ /^(for|foreach|given|if|unless|until|while)$/ ) ||
1427             $word1 eq 'return'
1428             ) &&
1429             (
1430             $word1 eq 'next' ||
1431             $word1 eq 'last' ||
1432             $word1 eq 'redo' ||
1433             $word1 eq 'return'
1434             ) )
1435             {
1436             # We add our special return value. Notice that we use 'return' and not
1437             # 'CORE::return'. See below why.
1438             # my $break_code = qq{return( '__} . uc( $word1 ) . qq{__' )};
1439 70 100       291 my $break_code = q{$Nice::Try::BREAK='} . $word1 . ( $word1 eq 'return' ? "', $e" : qq{', return} );
1440             # e.g. next if( $i == 2 );
1441             # next and if are both treated as 'word' by PPI
1442 70 100       1724 if( scalar( @$words ) > 1 )
1443             {
1444 10         30 ( my $ct = $e->content ) =~ s/^(next|last|redo)//;
1445 10         777 $break_code .= $ct;
1446             }
1447             else
1448             {
1449 60         113 $break_code .= ';'
1450             }
1451 70 50       176 $self->_message( 5, "Replacing this node with: $break_code" ) if( $self->{debug} >= 5 );
1452            
1453 70         325 my $break_doc = PPI::Document->new( \$break_code, readonly => 1 );
1454 70         139830 my $new_elem = $break_doc->first_element;
1455             # $self->_browse( $new_elem );
1456 70         418 $new_elem->remove;
1457 70 50   0   2888 $self->_message( 5, "New element is object '", sub{ overload::StrVal( $new_elem ) }, "' -> $new_elem" ) if( $self->{debug} >= 5 );
  0         0  
1458             # Not yet implemented as of 2021-05-11 dixit PPI, so we use a hack to make it available anyhow
1459 70 50       162 $self->_message( 5, "Updated element now is '$e' for class '", $e->class, "' and parent class '", $e->parent->class, "'." ) if( $self->{debug} >= 5 );
1460 70         254 $e->replace( $new_elem );
1461             # 2021-05-12 (Jacques): I have to do this workaround, because weirdly enough
1462             # PPI (at least with PPI::Node version 1.270) will refuse to add our element
1463             # if the 'return' word is 'CORE::return' so, we add it without and change it after
1464             # $new_elem->first_element->set_content( 'CORE::return' );
1465             # $self->_message( 5, "return litteral value is: ", $new_elem->first_element->content );
1466             }
1467 82         2692 next;
1468             }
1469            
1470 5916 100 100     18607 if( $e->can('elements') && $e->elements )
1471             {
1472 981         8829 $self->_process_loop_breaks( $e );
1473             }
1474             }
1475             # $self->_message( 5, "Element now is: '", sub{ $elem->content }, "'" );
1476             # $self->_message( 5, "Element now is: '$elem'" );
1477             # $self->_browse( $elem );
1478 1215         2866 return( $elem );
1479             }
1480              
1481             ## Taken from PPI::Document
1482             sub _serialize
1483             {
1484 351     351   590 my $self = shift( @_ );
1485 351   50     1142 my $ppi = shift( @_ ) || return( '' );
1486 24     24   233 no warnings 'uninitialized';
  24         128  
  24         17248  
1487 351         979 my @tokens = $ppi->tokens;
1488              
1489             # The here-doc content buffer
1490 351         27586 my $heredoc = '';
1491              
1492             # Start the main loop
1493 351         522 my $output = '';
1494 351         1003 foreach my $i ( 0 .. $#tokens ) {
1495 6767         8334 my $Token = $tokens[$i];
1496              
1497             # Handle normal tokens
1498 6767 50       16740 unless ( $Token->isa('PPI::Token::HereDoc') ) {
1499 6767         11303 my $content = $Token->content;
1500              
1501             # Handle the trivial cases
1502 6767 50 33     22936 unless ( $heredoc ne '' and $content =~ /\n/ ) {
1503 6767         8931 $output .= $content;
1504 6767         9803 next;
1505             }
1506              
1507             # We have pending here-doc content that needs to be
1508             # inserted just after the first newline in the content.
1509 0 0       0 if ( $content eq "\n" ) {
1510             # Shortcut the most common case for speed
1511 0         0 $output .= $content . $heredoc;
1512             } else {
1513             # Slower and more general version
1514 0         0 $content =~ s/\n/\n$heredoc/;
1515 0         0 $output .= $content;
1516             }
1517              
1518 0         0 $heredoc = '';
1519 0         0 next;
1520             }
1521              
1522             # This token is a HereDoc.
1523             # First, add the token content as normal, which in this
1524             # case will definitely not contain a newline.
1525 0         0 $output .= $Token->content;
1526              
1527             # Now add all of the here-doc content to the heredoc buffer.
1528 0         0 foreach my $line ( $Token->heredoc ) {
1529 0         0 $heredoc .= $line;
1530             }
1531              
1532 0 0       0 if ( $Token->{_damaged} ) {
1533             # Special Case:
1534             # There are a couple of warning/bug situations
1535             # that can occur when a HereDoc content was read in
1536             # from the end of a file that we silently allow.
1537             #
1538             # When writing back out to the file we have to
1539             # auto-repair these problems if we aren't going back
1540             # on to the end of the file.
1541              
1542             # When calculating $last_line, ignore the final token if
1543             # and only if it has a single newline at the end.
1544 0         0 my $last_index = $#tokens;
1545 0 0       0 if ( $tokens[$last_index]->{content} =~ /^[^\n]*\n$/ ) {
1546 0         0 $last_index--;
1547             }
1548              
1549             # This is a two part test.
1550             # First, are we on the last line of the
1551             # content part of the file
1552             my $last_line = List::Util::none {
1553 0 0   0   0 $tokens[$_] and $tokens[$_]->{content} =~ /\n/
1554 0         0 } (($i + 1) .. $last_index);
1555 0 0       0 if ( ! defined $last_line ) {
1556             # Handles the null list case
1557 0         0 $last_line = 1;
1558             }
1559              
1560             # Secondly, are their any more here-docs after us,
1561             # (with content or a terminator)
1562             my $any_after = List::Util::any {
1563             $tokens[$_]->isa('PPI::Token::HereDoc')
1564             and (
1565 0         0 scalar(@{$tokens[$_]->{_heredoc}})
1566             or
1567             defined $tokens[$_]->{_terminator_line}
1568             )
1569 0 0 0 0   0 } (($i + 1) .. $#tokens);
  0         0  
1570 0 0       0 if ( ! defined $any_after ) {
1571             # Handles the null list case
1572 0         0 $any_after = '';
1573             }
1574              
1575             # We don't need to repair the last here-doc on the
1576             # last line. But we do need to repair anything else.
1577 0 0 0     0 unless ( $last_line and ! $any_after ) {
1578             # Add a terminating string if it didn't have one
1579 0 0       0 unless ( defined $Token->{_terminator_line} ) {
1580 0         0 $Token->{_terminator_line} = $Token->{_terminator};
1581             }
1582              
1583             # Add a trailing newline to the terminating
1584             # string if it didn't have one.
1585 0 0       0 unless ( $Token->{_terminator_line} =~ /\n$/ ) {
1586 0         0 $Token->{_terminator_line} .= "\n";
1587             }
1588             }
1589             }
1590              
1591             # Now add the termination line to the heredoc buffer
1592 0 0       0 if ( defined $Token->{_terminator_line} ) {
1593 0         0 $heredoc .= $Token->{_terminator_line};
1594             }
1595             }
1596              
1597             # End of tokens
1598              
1599 351 50       865 if ( $heredoc ne '' ) {
1600             # If the file doesn't end in a newline, we need to add one
1601             # so that the here-doc content starts on the next line.
1602 0 0       0 unless ( $output =~ /\n$/ ) {
1603 0         0 $output .= "\n";
1604             }
1605              
1606             # Now we add the remaining here-doc content
1607             # to the end of the file.
1608 0         0 $output .= $heredoc;
1609             }
1610              
1611 351         1040 $output;
1612             }
1613              
1614              
1615             {
1616             package # hide from PAUSE
1617             Nice::Try::ScopeGuard;
1618              
1619             # older versions of perl have an issue with $@ during global destruction
1620 24 50   24   224 use constant UNSTABLE_DOLLARAT => ("$]" < '5.013002') ? 1 : 0;
  24         60  
  24         12581  
1621              
1622             sub _new
1623             {
1624 14     14   14689 my $this = shift( @_ );
1625 14   33     129 return( bless( [ @_ ] => ( ref( $this ) || $this ) ) );
1626             }
1627              
1628             sub DESTROY
1629             {
1630 14     14   1360 my( $code, @args ) = @{ $_[0] };
  14         66  
1631             # save the current exception to make it available in the finally sub,
1632             # and to restore it after the eval
1633 14         31 my $err = $@;
1634 14         22 local $@ if( UNSTABLE_DOLLARAT );
1635             CORE::eval
1636             {
1637 14         26 $@ = $err;
1638 14         38 $code->( @args );
1639 13         1297 1;
1640             }
1641             or do
1642 14 100       24 {
1643 1 50       25 CORE::warn
1644             "Execution of finally() block $code resulted in an exception, which "
1645             . '*CAN NOT BE PROPAGATED* due to fundamental limitations of Perl. '
1646             . 'Your program will continue as if this event never took place. '
1647             . "Original exception text follows:\n\n"
1648             . (defined $@ ? $@ : '$@ left undefined...')
1649             . "\n"
1650             ;
1651             };
1652             # maybe unnecessary?
1653 14         159 $@ = $err;
1654             }
1655             }
1656              
1657             {
1658             package
1659             Nice::Try::ObjectContext;
1660              
1661             sub new
1662             {
1663 2     2   22674 my $that = shift( @_ );
1664             # print( STDERR "Got here in Nice::Try::ObjectContext->new with args '", join( "', '", @_ ), "'\n" );
1665 2   33     36 return( bless( { val => [@_] } => ( ref( $that ) || $that ) ) );
1666             }
1667              
1668             sub callback
1669             {
1670 2     2   5 my $self = shift( @_ );
1671             # print( STDERR "Got here in Nice::Try::ObjectContext->dummy with args '", join( "', '", @_ ), "'\n" );
1672 2         21 return( $self->{val}->[0] );
1673             }
1674             }
1675              
1676             {
1677             package
1678             PPI::Element;
1679            
1680 24     24   235 no warnings 'redefine';
  24         72  
  24         4025  
1681             sub replace {
1682 70 50   70 1 221 my $self = ref $_[0] ? shift : return undef;
1683             # If our object and the other are not of the same class, PPI refuses to replace
1684             # to avoid damages to perl code
1685             # my $other = _INSTANCE(shift, ref $self) or return undef;
1686 70         99 my $other = shift;
1687             # die "The ->replace method has not yet been implemented";
1688 70         206 $self->parent->__replace_child( $self, $other );
1689 70         2571 1;
1690             }
1691             }
1692              
1693             1;
1694              
1695             # XXX POD
1696             __END__