File Coverage

blib/lib/Nice/Try.pm
Criterion Covered Total %
statement 486 622 78.1
branch 222 406 54.6
condition 99 172 57.5
subroutine 36 45 80.0
pod 4 7 57.1
total 847 1252 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.4
4             ## Copyright(c) 2023 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest
6             ## Created 2020/05/17
7             ## Modified 2023/05/06
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   309 require 5.16.0;
17 24     24   2456388 use strict;
  24         300  
  24         730  
18 24     24   136 use warnings;
  24         53  
  24         662  
19 24     24   119 use warnings::register;
  24         45  
  24         3188  
20 24         2894 use vars qw(
21             $CATCH $DIED $EXCEPTION $FINALLY $HAS_CATCH @RETVAL $SENTINEL $TRY $WANTARRAY
22             $VERSION $ERROR
23 24     24   155 );
  24         48  
24             # XXX Only for debugging
25             # use Devel::Confess;
26 24     24   12909 use PPI;
  24         2800875  
  24         1041  
27 24     24   11778 use Filter::Util::Call;
  24         18755  
  24         1714  
28 24     24   180 use Scalar::Util ();
  24         50  
  24         393  
29 24     24   121 use List::Util ();
  24         53  
  24         496  
30 24     24   11304 use Want ();
  24         42095  
  24         1578  
31 24         93 our $VERSION = 'v1.3.4';
32 24         44 our $ERROR;
33 24         510 our( $CATCH, $DIED, $EXCEPTION, $FINALLY, $HAS_CATCH, @RETVAL, $SENTINEL, $TRY, $WANTARRAY );
34             }
35              
36 24     24   130 use strict;
  24         63  
  24         488  
37 24     24   192 use warnings;
  24         58  
  24         47098  
38              
39             # Taken from Try::Harder version 0.005
40             our $SENTINEL = bless( {} => __PACKAGE__ . '::SENTINEL' );
41              
42             sub import
43             {
44 25     25   795 my( $this, @arguments ) = @_ ;
45 25         82 my $class = CORE::caller();
46 25         161 my $hash = { @arguments };
47 25 50       144 $hash->{debug} = 0 if( !CORE::exists( $hash->{debug} ) );
48 25 50       125 $hash->{no_filter} = 0 if( !CORE::exists( $hash->{no_filter} ) );
49 25 50       74 $hash->{debug_code} = 0 if( !CORE::exists( $hash->{debug_code} ) );
50 25 50       82 $hash->{debug_dump} = 0 if( !CORE::exists( $hash->{debug_dump} ) );
51 25 50       74 $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         48 $hash->{is_tied} = 0;
55 25 50 33     684 if( $class->can( 'TIESCALAR' ) || $class->can( 'TIEHASH' ) || $class->can( 'TIEARRAY' ) )
      33        
56             {
57 0         0 $hash->{is_tied} = 1;
58             }
59 25         153 require overload;
60 25 50       137 $hash->{is_overloaded} = overload::Overloaded( $class ) ? 1 : 0;
61 25         1848 $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     204 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 10 my $where = shift( @_ );
76 6         8 my $n = shift( @_ );
77             # Offsetting our internal call frames
78 6         19 my $map =
79             {
80             try => 3,
81             catch => 3,
82             finally => 5,
83             };
84 6 100       42 my @info = defined( $n ) ? CORE::caller( int( $n ) + $map->{ $where } ) : CORE::caller( 1 + $map->{ $where } );
85 6         34 return( @info );
86             }
87              
88 3     3 0 1567 sub caller_try { return( &Nice::Try::caller( try => @_ ) ); }
89              
90 1     1 0 1312 sub caller_catch { return( &Nice::Try::caller( catch => @_ ) ); }
91              
92 2     2 0 10 sub caller_finally { return( &Nice::Try::caller( finally => @_ ) ); }
93              
94             sub filter
95             {
96 43     43 1 80078 my( $self ) = @_ ;
97 43         91 my( $status, $last_line );
98 43         76 my $line = 0;
99 43         99 my $code = '';
100 43 50       308 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         338 while( $status = filter_read() )
108             {
109             # Error
110 2489 50       3688 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         2735 $line++;
116             # if( /^__(?:DATA|END)__/ )
117             # {
118             # $last_line = $_;
119             # last;
120             # }
121 2489         3099 $code .= $_;
122 2489         5058 $_ = '';
123             }
124 43 100       38452 return( $line ) if( !$line );
125 25 50       81 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         199 $code = ' ' . $code;
131 25 50       109 $self->_message( 4, "Processing $line lines of code." ) if( $self->{debug} >= 4 );
132 25   50     389 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       2342042 $self->_browse( $doc ) if( $self->{debug_dump} );
136 25 100       163 if( $doc = $self->_parse( $doc ) )
137             {
138 21         211 $_ = $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         1069 $_ = $code;
147             # $status = -1;
148             # filter_del();
149             }
150 25 50       115418 if( CORE::length( $last_line ) )
151             {
152 0         0 $_ .= $last_line;
153             }
154             }
155 25 50       61422 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       168 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         22903 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   4796 my $self = shift( @_ );
265 96 50       524 my $level = $_[0] =~ /^\d+$/ ? shift( @_ ) : 0;
266 96 50       308 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   504 my $self = shift( @_ );
287 267         453 my $elem = shift( @_ );
288 24     24   256 no warnings 'uninitialized';
  24         79  
  24         78148  
289 267 50 33     1956 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   439914 my( $top, $this ) = @_;
297 25549   100     42698 return( $this->class eq 'PPI::Statement' && substr( $this->content, 0, 3 ) eq 'try' );
298 267         1868 });
299 267 50       4575 return( $self->_error( "Failed to find any try-catch clause: $@" ) ) if( !defined( $ref ) );
300 267 50 66     1012 $self->_messagef( 4, "Found %d match(es)", scalar( @$ref ) ) if( $ref && ref( $ref ) && $self->{debug} >= 4 );
      66        
301 267 100 66     1248 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       108 $self->_message( 3, "Checking for consecutive try-catch blocks in results found by PPI" ) if( $self->{debug} >= 3 );
308 25         85 foreach my $this ( @$ref )
309             {
310 118         1145 my( @block_children ) = $this->children;
311 118 50       946 next if( !scalar( @block_children ) );
312 118         190 my $tmp_ref = [];
313             ## to contain all the nodes to move
314 118         176 my $tmp_nodes = [];
315 118         198 my $prev_sib = $block_children[0];
316 118         220 push( @$tmp_nodes, $prev_sib );
317 118         156 my $sib;
318 118         381 while( $sib = $prev_sib->next_sibling )
319             {
320             # We found a try-catch block. Move the buffer to $alt_ref
321 1538 100 100     37598 if( $sib->class eq 'PPI::Token::Word' && $sib->content eq 'try' )
322             {
323             # Look ahead for a block...
324 2         22 my $next = $sib->snext_sibling;
325 2 50 33     88 if( $next && $next->class eq 'PPI::Structure::Block' )
326             {
327 2 50       16 $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       6 $self->_messagef( 3, "Saving previous %d nodes collected.", scalar( @$tmp_nodes ) ) if( $self->{debug} >= 3 );
330 2         7 push( @$tmp_ref, $tmp_nodes );
331 2         14 $tmp_nodes = [];
332             }
333             }
334 1538         6812 push( @$tmp_nodes, $sib );
335 1538         3111 $prev_sib = $sib;
336             }
337 118 50       3722 $self->_messagef( 3, "Saving last %d nodes collected.", scalar( @$tmp_nodes ) ) if( $self->{debug} >= 3 );
338 118         215 push( @$tmp_ref, $tmp_nodes );
339 118 50       273 $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       280 if( scalar( @$tmp_ref ) > 1 )
342             {
343 2         4 my $last_obj = $this;
344 2         4 my $spaces = [];
345 2         12 foreach my $arr ( @$tmp_ref )
346             {
347 4 50       51 $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       10 $self->_message( 4, "Checking first level objects collected." ) if( $self->{debug} >= 4 );
351 4         7 my $last_control = '';
352 4         5 my $last_block;
353 4         10 my $last = {};
354 4         7 foreach my $o ( @$arr )
355             {
356             # $self->_message( 4, "Found object '$o' of class '", $o->class, "' (", overload::StrVal( $o ), ")." );
357 57 100 100     266 if( $o->class eq 'PPI::Structure::Block' && $last_control )
    100          
358             {
359 8         54 $last->{block} = $o;
360 8         11 $last->{control} = $last_control;
361 8         16 $last_control = '';
362             }
363             elsif( $o->class eq 'PPI::Token::Word' )
364             {
365 11         89 my $ct = $o->content;
366 11 100 100     65 if( $ct eq 'try' || $ct eq 'catch' || $ct eq 'finally' )
      66        
367             {
368 8         16 $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         38 my $insignificants = [];
376 4         10 while( scalar( @$arr ) > 0 )
377             {
378 25         899 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     65 last if( $o->class eq 'PPI::Structure::Block' && Scalar::Util::refaddr( $o ) eq Scalar::Util::refaddr( $last->{block} ) );
386 21         114 unshift( @$insignificants, pop( @$arr )->remove );
387             }
388 4 50       41 $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         651 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         18635 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         155 foreach my $o ( @$arr )
402             {
403             # We remove the object from its parent, now that it has become useless
404 36   50     960 my $old = $o->remove || die( "Unable to remove element '$o'\n" );
405             }
406 4         113 my $err = '';
407 4 0       12 $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       10 $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         11 $rc = $last_obj->__insert_after( $st );
414             }
415             else
416             {
417 2         15 $rc = $last_obj->insert_after( $st );
418             }
419            
420 4 50       179 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         8 $last_obj = $st;
431 4 50       11 if( scalar( @$insignificants ) )
432             {
433 4 50       10 $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       53 $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         29 {
440             $rc = $last_obj->insert_after( $o ) ||
441             do
442 21   33     69 {
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       1143 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       59 $o->parent( $last_obj->parent ) if( !$o->parent );
467 21         106 $last_obj = $o;
468             }
469             }
470             }
471 4 50       13 die( $err ) if( length( $err ) );
472 4         19 push( @$alt_ref, $st );
473             }
474 2         41 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         406 push( @$alt_ref, $this );
481             }
482             }
483 25 50       338 $self->_messagef( 3, "Results found increased from %d to %d results.", scalar( @$ref ), scalar( @$alt_ref ) ) if( $self->{debug} >= 3 );
484 25 100       103 @$ref = @$alt_ref if( scalar( @$alt_ref ) > scalar( @$ref ) );
485            
486             # $self->_message( 3, "Script code is now:\n'$elem'" );
487            
488 25         98 foreach my $this ( @$ref )
489             {
490 120 50       17820 $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         471 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         2847 my $try_block_ref = [];
495             # Contains the finally block reference
496 120         204 my $fin_block_ref = [];
497 120         217 my $nodes_to_replace = [];
498 120         195 my $catch_def = [];
499             # Replacement data
500 120         183 my $repl = [];
501 120         183 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         281 my $ct = "$this";
506             # $self->_message( 3, "Checking sibling elements for '$ct'" );
507 120         39715 my( @block_children ) = $this->children;
508 120 100       887 next if( !scalar( @block_children ) );
509 115         231 my $prev_sib = $block_children[0];
510 115         313 push( @$nodes_to_replace, $prev_sib );
511 115         227 my( $inside_catch, $inside_finally );
512 115         212 my $temp = {};
513             # Buffer of nodes found in between blocks
514 115         176 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         180 my $nl_counter = 0;
517 115         184 my $sib;
518 115         343 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     37035 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     665 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         31152 $temp->{block} = $sib;
529 113         241 push( @$try_block_ref, $temp );
530 113         226 $temp = {};
531 113 50       287 if( scalar( @$buff ) )
532             {
533 113         230 push( @$nodes_to_replace, @$buff );
534 113         255 $buff = [];
535             }
536 113         220 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         1240 $temp->{open_curly_nl}++;
542 68         159 push( @$buff, $sib );
543             }
544             ## We skip anything else until we find that try block
545             else
546             {
547 115         3021 push( @$buff, $sib );
548 115         178 $prev_sib = $sib;
549 115         355 next;
550             }
551             }
552             elsif( $sib->class eq 'PPI::Token::Word' && $sib->content eq 'catch' )
553             {
554 129         1044 $inside_catch++;
555 129 100       307 if( scalar( @$buff ) )
556             {
557 125         286 push( @$nodes_to_replace, @$buff );
558 125         239 $buff = [];
559             }
560 129         214 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     2083 if( $sib->class eq 'PPI::Structure::List' )
    100          
    100          
567             {
568 96         395 $temp->{var} = $sib;
569 96         177 push( @$nodes_to_replace, $sib );
570             }
571             elsif( $sib->class eq 'PPI::Structure::Block' )
572             {
573 129         792 $temp->{block} = $sib;
574 129 100       291 if( scalar( @$catch_def ) )
575             {
576 24         37 $catch_def->[-1]->{close_curly_nl} = $nl_counter;
577             }
578             else
579             {
580 105         199 $try_block_ref->[-1]->{close_curly_nl} = $nl_counter;
581             }
582 129         195 $nl_counter = 0;
583 129         276 push( @$catch_def, $temp );
584 129         204 $temp = {};
585 129         178 $inside_catch = 0;
586 129         249 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         1334 $temp->{open_curly_nl}++;
592 87         182 push( @$nodes_to_replace, $sib );
593             }
594             else
595             {
596 136         1829 push( @$nodes_to_replace, $sib );
597             }
598             }
599             elsif( $sib->class eq 'PPI::Token::Word' && $sib->content eq 'finally' )
600             {
601 13         221 $inside_finally++;
602 13 50       33 if( scalar( @$buff ) )
603             {
604 13         30 push( @$nodes_to_replace, @$buff );
605 13         26 $buff = [];
606             }
607 13         25 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     227 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         87 $temp->{block} = $sib;
620 13 50       50 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         13 $catch_def->[-1]->{close_curly_nl} = $nl_counter;
627             }
628             else
629             {
630 6         13 $try_block_ref->[-1]->{close_curly_nl} = $nl_counter;
631             }
632 13         18 $nl_counter = 0;
633 13         24 push( @$fin_block_ref, $temp );
634 13         24 $temp = {};
635 13         22 $inside_finally = 0;
636 13         24 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         21 $temp->{open_curly_nl}++;
642 1         2 push( @$nodes_to_replace, $sib );
643             }
644             else
645             {
646 13         229 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         3666 $nl_counter++;
660 189         393 push( @$buff, $sib );
661             }
662             else
663             {
664 353         4520 push( @$buff, $sib );
665             }
666 1340         3039 $prev_sib = $sib;
667             }
668            
669 115 100       3566 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         217 my $fin_block = '';
673 115 100       294 if( scalar( @$fin_block_ref ) )
674             {
675 13         34 my $fin_def = $fin_block_ref->[0];
676 13         73 $self->_process_caller( finally => $fin_def->{block} );
677             ## my $finally_block = $fin_def->{block}->content;
678 13         116 my $finally_block = $self->_serialize( $fin_def->{block} );
679 13         149 $finally_block =~ s/^\{[[:blank:]]*|[[:blank:]]*\}$//gs;
680 13         46 $fin_block = <
681             CORE::local \$Nice::Try::FINALLY = Nice\::Try\::ScopeGuard->_new(sub __FINALLY_OPEN_NL__{ __BLOCK_PLACEHOLDER__ __FINALLY__CLOSE_NL__}, [\@_], \$Nice::Try::CATCH_DIED);
682             EOT
683 13 50       77 $fin_block =~ s/\n/ /gs unless( $self->{debug_code} );
684 13         57 $fin_block =~ s/__BLOCK_PLACEHOLDER__/$finally_block/gs;
685 13 100       34 if( $fin_def->{open_curly_nl} )
686             {
687 1         5 $fin_block =~ s/__FINALLY_OPEN_NL__/"\n" x $fin_def->{open_curly_nl}/gex;
  1         5  
688             }
689             else
690             {
691 12         64 $fin_block =~ s/__FINALLY_OPEN_NL__//gs;
692             }
693 13 50       39 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         43 $fin_block =~ s/__FINALLY__CLOSE_NL__//gs;
700             }
701             }
702              
703             # Found any try block at all?
704 115 100       272 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         312 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         229 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       1368 if( my $emb = $self->_parse( $try_def->{block} ) )
720             {
721 3         9 $try_def->{block} = $emb;
722             }
723            
724 113         374 $self->_process_loop_breaks( $try_def->{block} );
725 113         1218 $self->_process_caller( try => $try_def->{block} );
726            
727             ## my $try_block = $try_def->{block}->content;
728 113         321 my $try_block = $self->_serialize( $try_def->{block} );
729 113         8960 $try_block =~ s/^\{[[:blank:]]*|[[:blank:]]*\}$//gs;
730            
731 113         605 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::CATCH_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     968 if( !$self->{is_tied} && !$self->{dont_want} && !$self->{is_overloaded} )
      33        
749             {
750 113         1128 $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( 'BOOL' )
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         592 $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 = &\$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       4006 $try_sub =~ s/\n/ /gs unless( $self->{debug_code} );
856 113         1083 $try_sub =~ s/__BLOCK_PLACEHOLDER__/$try_block/gs;
857 113 100       376 if( $try_def->{open_curly_nl} )
858             {
859 68         320 $try_sub =~ s/__TRY_OPEN_NL__/"\n" x $try_def->{open_curly_nl}/gex;
  68         544  
860             }
861             else
862             {
863 45         429 $try_sub =~ s/__TRY_OPEN_NL__//gs;
864             }
865 113 100       366 if( $try_def->{close_curly_nl} )
866             {
867 103         460 $try_sub =~ s/__TRY__CLOSE_NL__/"\n" x $try_def->{close_curly_nl}/gex;
  103         676  
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       335 if( !$has_catch_clause )
876             {
877 8         84 $try_sub =~ s/__FINALLY_BLOCK__/$fin_block/gs;
878             }
879             # If it should not be here, remove the placeholder
880             else
881             {
882 105         816 $try_sub =~ s/__FINALLY_BLOCK__//gs;
883             }
884 113         483 push( @$repl, $try_sub );
885             }
886             else
887             {
888             # $self->_message( 3, "** No try block found!!" );
889 2         10 next;
890             }
891            
892 113         209 my $if_start = <
893             if( \$Nice::Try::DIED )
894             {
895             if( \$Nice::Try::HAS_CATCH )
896             {
897             EOT
898 113 50       598 $if_start =~ s/\n/ /gs unless( $self->{debug_code} );
899 113         255 push( @$catch_repl, $if_start );
900 113 100       302 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         152 my $else = 0;
906 105         303 for( my $i = 0; $i < $total_catch; $i++ )
907             {
908 129         218 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       378 if( my $emb = $self->_parse( $cdef->{block} ) )
912             {
913 1         5 $cdef->{block} = $emb;
914             }
915            
916 129 100       414 if( $cdef->{var} )
917             {
918 96         370 $cdef->{var}->prune( 'PPI::Token::Comment' );
919 96         34934 $cdef->{var}->prune( 'PPI::Token::Pod' );
920 96         31899 $self->_messagef( 3, "Catch assignment is: '%s'", $cdef->{var}->content );
921             # my $str = $cdef->{var}->content;
922 96         300 my $str = $self->_serialize( $cdef->{var} );
923 96         728 $str =~ s/^\([[:blank:]\h\v]*|[[:blank:]]*\)$//g;
924             # My::Exception $e
925 96 100       625 if( $str =~ /^(\S+)[[:blank:]\h\v]+(\$\S+)$/ )
    100          
    100          
    100          
    100          
926             {
927 15         82 @$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         27 @$cdef{qw( class var where )} = ( $1, $2, $3 );
932             }
933             elsif( $str =~ /^(\$\S+)[[:blank:]\h\v]+where[[:blank:]\h\v]+\{(.*?)\}$/ )
934             {
935 1         6 @$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         56 @$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   11177 @$cdef{qw( var class where )} = ( $+{var}, $+{class}, $+{where} );
  24         9085  
  24         40388  
  2         69  
944             }
945             else
946             {
947 65         171 $cdef->{var} = $str;
948             }
949             }
950             else
951             {
952             # $self->_message( 3, "No Catch assignment found" );
953             }
954            
955 129 50       476 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         189 my $cond;
965 129 100       297 if( $i == 0 )
    100          
966             {
967 105         182 $cond = 'if';
968             }
969             elsif( $i == ( $total_catch - 1 ) )
970             {
971             $cond = $total_catch == 1
972             ? 'if'
973             : $cdef->{class}
974 11 100       35 ? 'elsif'
    50          
975             : 'else';
976             }
977             else
978             {
979 13         22 $cond = 'elsif';
980             }
981             # $self->_message( 3, "\$i = $i, \$total_catch = $total_catch and cond = '$cond'" );
982             # my $block = $cdef->{block}->content;
983 129         368 $self->_process_loop_breaks( $cdef->{block} );
984 129         1690 $self->_process_caller( catch => $cdef->{block} );
985 129         350 my $block = $self->_serialize( $cdef->{block} );
986 129         3596 $block =~ s/^\{[[:blank:]]*|[[:blank:]]*\}$//gs;
987 129         302 my $catch_section = '';
988 129         419 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             eval
997             {
998             local \$\@ = \$Nice::Try::EXCEPTION;
999             if( CORE::defined( \$Nice::Try::WANT ) && CORE::length( \$Nice::Try::WANT ) )
1000             {
1001             if( \$Nice::Try::WANT eq 'OBJECT' )
1002             {
1003             \$Nice::Try::RETVAL[0] = Nice::Try::ObjectContext->new( \&\$Nice::Try::CATCH )->callback();
1004             }
1005             elsif( \$Nice::Try::WANT eq 'CODE' )
1006             {
1007             \$Nice::Try::RETVAL[0] = \$Nice::Try::NOOP->( \&\$Nice::Try::CATCH )->();
1008             }
1009             elsif( \$Nice::Try::WANT eq 'HASH' )
1010             {
1011             \@Nice::Try::RETVAL = \%{ \&\$Nice::Try::CATCH };
1012             }
1013             elsif( \$Nice::Try::WANT eq 'ARRAY' )
1014             {
1015             \@Nice::Try::RETVAL = \@{ \&\$Nice::Try::CATCH };
1016             }
1017             elsif( \$Nice::Try::WANT eq 'REFSCALAR' )
1018             {
1019             \$Nice::Try::RETVAL[0] = \${\&\$Nice::Try::CATCH};
1020             }
1021             elsif( \$Nice::Try::WANT eq 'GLOB' )
1022             {
1023             \$Nice::Try::RETVAL[0] = \*{ \&\$Nice::Try::CATCH };
1024             }
1025             elsif( \$Nice::Try::WANT eq 'LIST' )
1026             {
1027             \@Nice::Try::RETVAL = \&\$Nice::Try::CATCH;
1028             }
1029             elsif( \$Nice::Try::WANT eq 'BOOLEAN' )
1030             {
1031             my \$this = \&\$Nice::Try::CATCH ? 1 : 0;
1032             \$Nice::Try::RETVAL[0] = \$Nice::Try::VOID[0] if( scalar( \@Nice::Try::VOID ) );
1033             }
1034             elsif( \$Nice::Try::WANT eq 'VOID' )
1035             {
1036             \@Nice::Try::VOID = \&\$Nice::Try::CATCH;
1037             }
1038             elsif( \$Nice::Try::WANT eq 'SCALAR' )
1039             {
1040             \$Nice::Try::RETVAL[0] = \&\$Nice::Try::CATCH;
1041             }
1042             }
1043             else
1044             {
1045             if( \$Nice::Try::WANTARRAY )
1046             {
1047             \@Nice::Try::RETVAL = \&\$Nice::Try::CATCH;
1048             }
1049             elsif( defined( \$Nice::Try::WANTARRAY ) )
1050             {
1051             \$Nice::Try::RETVAL[0] = \&\$Nice::Try::CATCH;
1052             }
1053             else
1054             {
1055             \&\$Nice::Try::CATCH;
1056             }
1057             }
1058             };
1059             \$Nice::Try::CATCH_DIED = \$\@ if( \$\@ );
1060             EOT
1061 129 100       348 if( $cdef->{var} )
1062             {
1063 96         157 my $ex_var = $cdef->{var};
1064 96 100 100     475 if( $cdef->{class} && $cdef->{where} )
    100          
    100          
1065             {
1066 12         20 my $ex_class = $cdef->{class};
1067 12         34 my $eval = "q{CORE::local \$_ = \$Nice::Try::EXCEPTION; my $ex_var = \$Nice::Try::EXCEPTION; CORE::local \$\@ = \$Nice::Try::EXCEPTION; $cdef->{where}}";
1068 12         91 $catch_section = <
1069             ${cond}( Scalar::Util::blessed( \$Nice::Try::EXCEPTION ) && \$Nice::Try::EXCEPTION->isa( '$ex_class' ) && CORE::eval( $eval ) )
1070             {
1071             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1072             my $ex_var = \$Nice::Try::EXCEPTION;
1073             $catch_code
1074             }
1075             EOT
1076             }
1077             elsif( $cdef->{class} )
1078             {
1079 18         42 my $ex_class = $cdef->{class};
1080             # Tilmann Haeberle (TH) 2021-03-25: Fix: properly test for exception class inheritance via ->isa
1081 18         200 $catch_section = <
1082             ${cond}( Scalar::Util::blessed( \$Nice::Try::EXCEPTION ) && \$Nice::Try::EXCEPTION->isa( '$ex_class' ) )
1083             {
1084             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1085             my $ex_var = \$Nice::Try::EXCEPTION;
1086             $catch_code
1087             }
1088             EOT
1089             }
1090             elsif( $cdef->{where} )
1091             {
1092 1         8 my $eval = "q{CORE::local \$_ = \$Nice::Try::EXCEPTION; my $ex_var = \$Nice::Try::EXCEPTION; CORE::local \$\@ = \$Nice::Try::EXCEPTION; $cdef->{where}}";
1093 1         12 $catch_section = <
1094             ${cond}( CORE::eval( $eval ) )
1095             {
1096             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1097             my $ex_var = \$Nice::Try::EXCEPTION;
1098             $catch_code
1099             }
1100             EOT
1101             }
1102             # No class, just variable assignment like $e or something
1103             else
1104             {
1105             # $self->_message( 3, "Called here for fallback for element No $i" );
1106 65 50       174 if( ++$else > 1 )
1107             {
1108             # CORE::warn( "Cannot have more than one falllback catch clause for block: ", $cdef->{block}->content, "\n" ) if( warnings::enabled );
1109 0 0       0 CORE::warn( "Cannot have more than one falllback catch clause for block: ", $self->_serialize( $cdef->{block} ), "\n" ) if( warnings::enabled );
1110             # Skip, not die. Not fatal, just ignored
1111 0         0 next;
1112             }
1113 65 100 66     222 $cond = "${cond}( 1 )" if( $cond eq 'if' || $cond eq 'elsif' );
1114             # push( @$catch_repl, <
1115 65         615 $catch_section = <
1116             ${cond}
1117             {
1118             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1119             my $ex_var = \$Nice::Try::EXCEPTION;
1120             $catch_code
1121             }
1122             EOT
1123             }
1124             }
1125             # No variable assignment like $e
1126             else
1127             {
1128 33 50 33     149 $cond = "${cond}( 1 )" if( $cond eq 'if' || $cond eq 'elsif' );
1129 33         272 $catch_section = <
1130             ${cond}
1131             {
1132             CORE::local \$\@ = \$Nice::Try::EXCEPTION;
1133             $catch_code
1134             }
1135             EOT
1136             }
1137 129 50       3726 $catch_section =~ s/\n/ /gs unless( $self->{debug_code} );
1138 129         896 $catch_section =~ s/__BLOCK_PLACEHOLDER__/$block/gs;
1139 129 100       380 if( $cdef->{open_curly_nl} )
1140             {
1141 87         370 $catch_section =~ s/__CATCH_OPEN_NL__/"\n" x $cdef->{open_curly_nl}/gex;
  87         547  
1142             }
1143             else
1144             {
1145 42         296 $catch_section =~ s/__CATCH_OPEN_NL__//gs;
1146             }
1147 129 100       350 if( $cdef->{close_curly_nl} )
1148             {
1149 27         115 $catch_section =~ s/__CATCH__CLOSE_NL__/"\n" x $cdef->{close_curly_nl}/gex;
  27         133  
1150             }
1151             else
1152             {
1153 102         678 $catch_section =~ s/__CATCH__CLOSE_NL__//gs;
1154             }
1155 129         603 push( @$catch_repl, $catch_section );
1156             }
1157             # End catch loop
1158             # Tilmann Haeberle (TH) 2021-03-25: Fix: put an else at the end to avoid 'fall_through' issue unless an else exists already
1159 105         175 my $if_end;
1160 105 100       229 if( $else )
1161             {
1162 65         110 $if_end = <
1163             }
1164             EOT
1165             }
1166             else
1167             {
1168 40         73 $if_end = <
1169             else
1170             {
1171             die( \$Nice::Try::EXCEPTION );
1172             }
1173             }
1174             EOT
1175             }
1176 105 50       478 $if_end =~ s/\n/ /g unless( $self->{debug_code} );
1177 105         240 push( @$catch_repl, $if_end );
1178             }
1179             # No catch clause
1180             else
1181             {
1182             # If the try-catch block is called inside an eval, propagate the exception
1183             # Otherwise, we just make the $@ available
1184 8         18 my $catch_else = <
1185             }
1186             else
1187             {
1188             if( CORE::defined( (CORE::caller(0))[3] ) && (CORE::caller(0))[3] eq '(eval)' )
1189             {
1190             CORE::die( \$Nice::Try::EXCEPTION );
1191             }
1192             else
1193             {
1194             \$\@ = \$Nice::Try::EXCEPTION;
1195             }
1196             }
1197             EOT
1198 8 50       64 $catch_else =~ s/\n/ /g unless( $self->{debug_code} );
1199 8         22 push( @$catch_repl, $catch_else );
1200             }
1201            
1202             # Add
1203 113 50       2159 my $catch_res = scalar( @$catch_repl ) ? join( '', @$catch_repl ) : '';
1204 113 50       346 push( @$repl, $catch_res ) if( $catch_res );
1205             # Closing the If DIED condition
1206 113         227 push( @$repl, "\};" );
1207              
1208             # If there is a catch clause, we put the final block here, if any
1209 113 100 100     471 if( $has_catch_clause && CORE::length( $fin_block ) )
1210             {
1211 7         13 push( @$repl, $fin_block );
1212             }
1213            
1214             # After the finally block has been registered, we will die if catch had a fatal error
1215 113         178 my $catch_dies = <
1216             if( defined( \$Nice::Try::CATCH_DIED ) )
1217             {
1218             die( \$Nice::Try::CATCH_DIED );
1219             }
1220             EOT
1221 113 50       585 $catch_dies =~ s/\n/ /gs unless( $self->{debug_code} );
1222 113         228 push( @$repl, $catch_dies );
1223            
1224 113         398 my $last_return_block = <
1225             if( ( CORE::defined( \$Nice::Try::WANTARRAY ) || ( defined( \$Nice::Try::BREAK ) && \$Nice::Try::BREAK eq 'return' ) ) and
1226             (
1227             !Scalar::Util::blessed( \$Nice::Try::RETVAL[0] ) or
1228             ( Scalar::Util::blessed( \$Nice::Try::RETVAL[0] ) && !\$Nice::Try::RETVAL[0]->isa( 'Nice::Try::SENTINEL' ) )
1229             ) )
1230             {
1231             if( !CORE::defined( \$Nice::Try::BREAK ) || \$Nice::Try::BREAK eq 'return' )
1232             {
1233             if( CORE::defined( \$Nice::Try::WANT ) && CORE::length( \$Nice::Try::WANT ) )
1234             {
1235             if( \$Nice::Try::WANT eq 'LIST' )
1236             {
1237             CORE::return( \@Nice::Try::RETVAL );
1238             }
1239             elsif( \$Nice::Try::WANT eq 'VOID' )
1240             {
1241             if( CORE::defined( \$Nice::Try::RETVAL[0] ) && \$Nice::Try::RETVAL[0] eq '__NEXT__' )
1242             {
1243             \$Nice::Try::BREAK = 'next';
1244             }
1245             elsif( CORE::defined( \$Nice::Try::RETVAL[0] ) && \$Nice::Try::RETVAL[0] eq '__LAST__' )
1246             {
1247             \$Nice::Try::BREAK = 'last';
1248             }
1249             elsif( CORE::defined( \$Nice::Try::RETVAL[0] ) && \$Nice::Try::RETVAL[0] eq '__REDO__' )
1250             {
1251             \$Nice::Try::BREAK = 'redo';
1252             }
1253             elsif( defined( \$Nice::Try::BREAK ) && \$Nice::Try::BREAK eq 'return' )
1254             {
1255             CORE::return( \$Nice::Try::RETVAL[0] );
1256             }
1257             }
1258             elsif( \$Nice::Try::WANT eq 'OBJECT' )
1259             {
1260             CORE::return( \$Nice::Try::RETVAL[0] );
1261             }
1262             elsif( \$Nice::Try::WANT eq 'REFSCALAR' )
1263             {
1264             CORE::return( \\\$Nice::Try::RETVAL[0] );
1265             }
1266             elsif( \$Nice::Try::WANT eq 'SCALAR' )
1267             {
1268             CORE::return( \$Nice::Try::RETVAL[0] );
1269             }
1270             elsif( \$Nice::Try::WANT eq 'BOOLEAN' )
1271             {
1272             CORE::return( \$Nice::Try::RETVAL[0] );
1273             }
1274             elsif( \$Nice::Try::WANT eq 'CODE' )
1275             {
1276             CORE::return( \$Nice::Try::RETVAL[0] );
1277             }
1278             elsif( \$Nice::Try::WANT eq 'HASH' )
1279             {
1280             CORE::return( { \@Nice::Try::RETVAL } );
1281             }
1282             elsif( \$Nice::Try::WANT eq 'ARRAY' )
1283             {
1284             CORE::return( \\\@Nice::Try::RETVAL );
1285             }
1286             elsif( \$Nice::Try::WANT eq 'GLOB' )
1287             {
1288             CORE::return( \$Nice::Try::RETVAL[0] );
1289             }
1290             }
1291             else
1292             {
1293             CORE::return( \$Nice::Try::WANTARRAY ? \@Nice::Try::RETVAL : \$Nice::Try::RETVAL[0] );
1294             }
1295             }
1296             }
1297             elsif( scalar( \@Nice::Try::VOID ) && ( !Scalar::Util::blessed( \$Nice::Try::VOID[0] ) || ( Scalar::Util::blessed( \$Nice::Try::VOID[0] ) && !\$Nice::Try::VOID[0]->isa( 'Nice::Try::SENTINEL' ) ) ) )
1298             {
1299             CORE::return( scalar( \@Nice::Try::VOID ) > 1 ? \@Nice::Try::VOID : \$Nice::Try::VOID[0] );
1300             }
1301             EOT
1302 113 50       2460 $last_return_block =~ s/\n/ /gs unless( $self->{debug_code} );
1303 113         286 push( @$repl, $last_return_block );
1304 113         1984 my $try_catch_code = join( '', @$repl );
1305             # my $token = PPI::Token->new( "; \{ $try_catch_code \}" ) || die( "Unable to create token" );
1306             # XXX 2021-05-11 (Jacques): Need to remove blocks so that next or last statements can be effective.
1307 113         305 my $envelop = <
1308             ; CORE::local( \$Nice::Try::BREAK, \@Nice::Try::LAST_VAL );
1309             \{
1310             __TRY_CATCH_CODE__
1311             \}
1312             if( CORE::defined( \$Nice::Try::BREAK ) )
1313             {
1314             if( \$Nice::Try::BREAK eq 'next' )
1315             {
1316             CORE::next;
1317             }
1318             elsif( \$Nice::Try::BREAK eq 'last' )
1319             {
1320             CORE::last;
1321             }
1322             elsif( \$Nice::Try::BREAK eq 'redo' )
1323             {
1324             CORE::redo;
1325             }
1326             }
1327             no warnings 'void';
1328             CORE::scalar( \@Nice::Try::LAST_VAL ) > 1 ? \@Nice::Try::LAST_VAL : \$Nice::Try::LAST_VAL[0];
1329             EOT
1330 113 50       1151 $envelop =~ s/\n/ /gs unless( $self->{debug_code} );
1331 113         1891 $envelop =~ s/__TRY_CATCH_CODE__/$try_catch_code/;
1332 113   50     571 my $token = PPI::Token->new( $envelop ) || die( "Unable to create token" );
1333 113         4029 $token->set_class( 'Structure' );
1334             # $self->_messagef( 3, "Token is '$token' and of class '%s' and inherit from PPI::Token? %s", $token->class, ($token->isa( 'PPI::Token' ) ? 'yes' : 'no' ) );
1335 113   50     2109 my $struct = PPI::Structure->new( $token ) || die( "Unable to create PPI::Structure element" );
1336             # $self->_message( 3, "Resulting try-catch block is:\n'$token'" );
1337 113         4085 my $orig_try_catch_block = join( '', @$nodes_to_replace );
1338             # $self->_message( 3, "Original try-catch block is:\n'$orig_try_catch_block'" );
1339             # $self->_messagef( 3, "Element before our try-catch block is of class %s with value '%s'", $element_before_try->class, $element_before_try->content );
1340 113         33272 my $rc;
1341 113 50       367 if( !( $rc = $element_before_try->insert_after( $token ) ) )
1342             {
1343             # $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' );
1344 0         0 $self->_error( "Failed to add replacement code of class '", $token->class, "' after '$element_before_try'" );
1345 0         0 next;
1346             }
1347 113 0       6704 $self->_message( 3, "Return code is defined? ", defined( $rc ) ? "yes" : "no" ) if( $self->{debug} >= 3 );
    50          
1348            
1349 113         390 for( my $k = 0; $k < scalar( @$nodes_to_replace ); $k++ )
1350             {
1351 1293         78718 my $e = $nodes_to_replace->[$k];
1352             ## $self->_messagef( 4, "[$k] Removing node: $e" );
1353 1293 50       2530 $e->delete || warn( "Could not remove node No $k: '$e'\n" );
1354             }
1355             }
1356             # End foreach catch found
1357            
1358             # $self->_message( 3, "\n\nResulting code is\n", $elem->content );
1359 25         7837 return( $elem );
1360             }
1361              
1362             # .Element: [11] class PPI::Token::Word, value caller
1363             # .Element: [11] class PPI::Structure::List, value (1)
1364             #
1365             # ..Element: [12] class PPI::Token::Word, value caller
1366             # ..Element: [12] class PPI::Token::Structure, value ;
1367              
1368             sub _process_caller
1369             {
1370 1428     1428   2014 my $self = shift( @_ );
1371 1428         1875 my $where = shift( @_ );
1372 1428   50     3636 my $elem = shift( @_ ) || return( '' );
1373 24     24   246 no warnings 'uninitialized';
  24         76  
  24         5655  
1374 1428 100       2759 return( $elem ) if( !$elem->children );
1375 1419         7215 foreach my $e ( $elem->elements )
1376             {
1377 7077   50     19250 my $content = $e->content // '';
1378             # $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 );
1379 7077         73842 my $class = $e->class;
1380 7077 100 100     23886 if( $class eq 'PPI::Token::Word' && $content =~ /^(?:CORE\::)?(?:GLOBAL\::)?caller$/ )
1381             {
1382             # $self->_message( 4, "Found caller, replacing with ", 'Nice::Try::caller_' . $where );
1383 5         21 $e->set_content( 'Nice::Try::caller_' . $where );
1384             }
1385            
1386 7077 100 100     18930 if( $e->can('elements') && $e->elements )
1387             {
1388 1173         9502 $self->_process_caller( $where => $e );
1389             }
1390             }
1391             # $self->_message( 5, "Element now is: '$elem'" );
1392             # $self->_browse( $elem );
1393 1419         2581 return( $elem );
1394             }
1395              
1396             sub _process_loop_breaks
1397             {
1398 1223     1223   1831 my $self = shift( @_ );
1399 1223   50     3165 my $elem = shift( @_ ) || return( '' );
1400 24     24   197 no warnings 'uninitialized';
  24         58  
  24         21894  
1401 1223 100       2725 return( $elem ) if( !$elem->children );
1402 1215 50       6514 $self->_message( 5, "Checking ", scalar( $elem->elements ), " elements for '$elem'" ) if( $self->{debug} >= 5 );
1403 1215         2389 foreach my $e ( $elem->elements )
1404             {
1405 5998   50     19418 my $content = $e->content // '';
1406 5998 0       67017 $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          
1407 5998         10734 my $class = $e->class;
1408             # 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.
1409 5998 50 66     36292 if( $class eq 'PPI::Structure::For' ||
    50 66        
    100 33        
      66        
1410             ( $class eq 'PPI::Statement::Compound' &&
1411             CORE::defined( $e->first_element->content ) &&
1412             $e->first_element->content =~ /^(for|foreach|while)$/ ) )
1413             {
1414             # $self->_message( 6, "Skipping it. Its first word was '", $e->first_element->content, "'" );
1415 0         0 next;
1416             }
1417             elsif( $class eq 'PPI::Token::Word' && $content =~ /^(?:CORE\::)?(?:GLOBAL\::)?(next|last|redo)$/ )
1418             {
1419 0 0       0 $self->_message( 5, "Found loop keyword '$content'." ) if( $self->{debug} >= 5 );
1420             # $e->set_content( qq{CORE::return( '__} . uc( $1 ) . qq{__' )} );
1421             # $e->set_content( q{$Nice::Try::BREAK='__} . uc( $1 ) . qq{__' ); return;} );
1422 0         0 my $break_code = q{$Nice::Try::BREAK='} . $1 . qq{', return;};
1423 0         0 my $break_doc = PPI::Document->new( \$break_code, readonly => 1 );
1424 0         0 my $new_elem = $break_doc->first_element;
1425             # $self->_browse( $new_elem );
1426 0         0 $new_elem->remove;
1427 0 0   0   0 $self->_message( 5, "New element is object '", sub{ overload::StrVal( $new_elem ) }, "' -> $new_elem" ) if( $self->{debug} >= 5 );
  0         0  
1428             # Not yet implemented as of 2021-05-11 dixit PPI, so we use a hack to make it available anyhow
1429 0         0 $e->replace( $new_elem );
1430 0 0       0 $self->_message( 5, "Loop keyword now replaced with '$e'." ) if( $self->{debug} >= 5 );
1431             }
1432             elsif( $class eq 'PPI::Statement::Break' )
1433             {
1434 82         283 my $words = $e->find( 'PPI::Token::Word' );
1435 82 50       27612 $self->_messagef( 5, "Found %d word elements inside break element.", scalar( @$words ) ) if( $self->{debug} >= 5 );
1436 82 50 50     375 my $word1 = ( scalar( @$words ) ? $words->[0]->content // '' : '' );
1437 82 100 50     483 my $word2 = ( scalar( @$words ) > 1 ? $words->[1]->content // '' : '' );
1438 82 50       268 $self->_message( 5, "Word 1 -> ", $word1 ) if( $self->{debug} >= 5 );
1439 82 50 33     211 $self->_message( 5, "Word 2 -> ", $word2 ) if( $self->{debug} >= 5 && scalar( @$words ) > 1 );
1440             # $self->_browse( $e );
1441             # If we found a break word without a label, i.e. next, last, redo,
1442             # we replace it with a special return statement
1443 82 50 100     779 if( (
      66        
      100        
1444             scalar( @$words ) == 1 ||
1445             ( scalar( @$words ) > 1 && $word2 =~ /^(for|foreach|given|if|unless|until|while)$/ ) ||
1446             $word1 eq 'return'
1447             ) &&
1448             (
1449             $word1 eq 'next' ||
1450             $word1 eq 'last' ||
1451             $word1 eq 'redo' ||
1452             $word1 eq 'return'
1453             ) )
1454             {
1455             # We add our special return value. Notice that we use 'return' and not
1456             # 'CORE::return'. See below why.
1457             # my $break_code = qq{return( '__} . uc( $word1 ) . qq{__' )};
1458 70 100       288 my $break_code = q{$Nice::Try::BREAK='} . $word1 . ( $word1 eq 'return' ? "', $e" : qq{', return} );
1459             # e.g. next if( $i == 2 );
1460             # next and if are both treated as 'word' by PPI
1461 70 100       1734 if( scalar( @$words ) > 1 )
1462             {
1463 10         25 ( my $ct = $e->content ) =~ s/^(next|last|redo)//;
1464 10         774 $break_code .= $ct;
1465             }
1466             else
1467             {
1468 60         110 $break_code .= ';'
1469             }
1470 70 50       188 $self->_message( 5, "Replacing this node with: $break_code" ) if( $self->{debug} >= 5 );
1471            
1472 70         310 my $break_doc = PPI::Document->new( \$break_code, readonly => 1 );
1473 70         140212 my $new_elem = $break_doc->first_element;
1474             # $self->_browse( $new_elem );
1475 70         451 $new_elem->remove;
1476 70 50   0   2820 $self->_message( 5, "New element is object '", sub{ overload::StrVal( $new_elem ) }, "' -> $new_elem" ) if( $self->{debug} >= 5 );
  0         0  
1477             # Not yet implemented as of 2021-05-11 dixit PPI, so we use a hack to make it available anyhow
1478 70 50       197 $self->_message( 5, "Updated element now is '$e' for class '", $e->class, "' and parent class '", $e->parent->class, "'." ) if( $self->{debug} >= 5 );
1479 70         242 $e->replace( $new_elem );
1480             # 2021-05-12 (Jacques): I have to do this workaround, because weirdly enough
1481             # PPI (at least with PPI::Node version 1.270) will refuse to add our element
1482             # if the 'return' word is 'CORE::return' so, we add it without and change it after
1483             # $new_elem->first_element->set_content( 'CORE::return' );
1484             # $self->_message( 5, "return litteral value is: ", $new_elem->first_element->content );
1485             }
1486 82         2693 next;
1487             }
1488            
1489 5916 100 100     18528 if( $e->can('elements') && $e->elements )
1490             {
1491 981         8626 $self->_process_loop_breaks( $e );
1492             }
1493             }
1494             # $self->_message( 5, "Element now is: '", sub{ $elem->content }, "'" );
1495             # $self->_message( 5, "Element now is: '$elem'" );
1496             # $self->_browse( $elem );
1497 1215         2841 return( $elem );
1498             }
1499              
1500             ## Taken from PPI::Document
1501             sub _serialize
1502             {
1503 351     351   581 my $self = shift( @_ );
1504 351   50     1056 my $ppi = shift( @_ ) || return( '' );
1505 24     24   207 no warnings 'uninitialized';
  24         63  
  24         16043  
1506 351         913 my @tokens = $ppi->tokens;
1507              
1508             # The here-doc content buffer
1509 351         26750 my $heredoc = '';
1510              
1511             # Start the main loop
1512 351         516 my $output = '';
1513 351         953 foreach my $i ( 0 .. $#tokens ) {
1514 6767         8211 my $Token = $tokens[$i];
1515              
1516             # Handle normal tokens
1517 6767 50       16709 unless ( $Token->isa('PPI::Token::HereDoc') ) {
1518 6767         11233 my $content = $Token->content;
1519              
1520             # Handle the trivial cases
1521 6767 50 33     23323 unless ( $heredoc ne '' and $content =~ /\n/ ) {
1522 6767         8679 $output .= $content;
1523 6767         9759 next;
1524             }
1525              
1526             # We have pending here-doc content that needs to be
1527             # inserted just after the first newline in the content.
1528 0 0       0 if ( $content eq "\n" ) {
1529             # Shortcut the most common case for speed
1530 0         0 $output .= $content . $heredoc;
1531             } else {
1532             # Slower and more general version
1533 0         0 $content =~ s/\n/\n$heredoc/;
1534 0         0 $output .= $content;
1535             }
1536              
1537 0         0 $heredoc = '';
1538 0         0 next;
1539             }
1540              
1541             # This token is a HereDoc.
1542             # First, add the token content as normal, which in this
1543             # case will definitely not contain a newline.
1544 0         0 $output .= $Token->content;
1545              
1546             # Now add all of the here-doc content to the heredoc buffer.
1547 0         0 foreach my $line ( $Token->heredoc ) {
1548 0         0 $heredoc .= $line;
1549             }
1550              
1551 0 0       0 if ( $Token->{_damaged} ) {
1552             # Special Case:
1553             # There are a couple of warning/bug situations
1554             # that can occur when a HereDoc content was read in
1555             # from the end of a file that we silently allow.
1556             #
1557             # When writing back out to the file we have to
1558             # auto-repair these problems if we aren't going back
1559             # on to the end of the file.
1560              
1561             # When calculating $last_line, ignore the final token if
1562             # and only if it has a single newline at the end.
1563 0         0 my $last_index = $#tokens;
1564 0 0       0 if ( $tokens[$last_index]->{content} =~ /^[^\n]*\n$/ ) {
1565 0         0 $last_index--;
1566             }
1567              
1568             # This is a two part test.
1569             # First, are we on the last line of the
1570             # content part of the file
1571             my $last_line = List::Util::none {
1572 0 0   0   0 $tokens[$_] and $tokens[$_]->{content} =~ /\n/
1573 0         0 } (($i + 1) .. $last_index);
1574 0 0       0 if ( ! defined $last_line ) {
1575             # Handles the null list case
1576 0         0 $last_line = 1;
1577             }
1578              
1579             # Secondly, are their any more here-docs after us,
1580             # (with content or a terminator)
1581             my $any_after = List::Util::any {
1582             $tokens[$_]->isa('PPI::Token::HereDoc')
1583             and (
1584 0         0 scalar(@{$tokens[$_]->{_heredoc}})
1585             or
1586             defined $tokens[$_]->{_terminator_line}
1587             )
1588 0 0 0 0   0 } (($i + 1) .. $#tokens);
  0         0  
1589 0 0       0 if ( ! defined $any_after ) {
1590             # Handles the null list case
1591 0         0 $any_after = '';
1592             }
1593              
1594             # We don't need to repair the last here-doc on the
1595             # last line. But we do need to repair anything else.
1596 0 0 0     0 unless ( $last_line and ! $any_after ) {
1597             # Add a terminating string if it didn't have one
1598 0 0       0 unless ( defined $Token->{_terminator_line} ) {
1599 0         0 $Token->{_terminator_line} = $Token->{_terminator};
1600             }
1601              
1602             # Add a trailing newline to the terminating
1603             # string if it didn't have one.
1604 0 0       0 unless ( $Token->{_terminator_line} =~ /\n$/ ) {
1605 0         0 $Token->{_terminator_line} .= "\n";
1606             }
1607             }
1608             }
1609              
1610             # Now add the termination line to the heredoc buffer
1611 0 0       0 if ( defined $Token->{_terminator_line} ) {
1612 0         0 $heredoc .= $Token->{_terminator_line};
1613             }
1614             }
1615              
1616             # End of tokens
1617              
1618 351 50       722 if ( $heredoc ne '' ) {
1619             # If the file doesn't end in a newline, we need to add one
1620             # so that the here-doc content starts on the next line.
1621 0 0       0 unless ( $output =~ /\n$/ ) {
1622 0         0 $output .= "\n";
1623             }
1624              
1625             # Now we add the remaining here-doc content
1626             # to the end of the file.
1627 0         0 $output .= $heredoc;
1628             }
1629              
1630 351         1010 $output;
1631             }
1632              
1633              
1634             {
1635             # NOTE: Nice::Try::ScopeGuard class
1636             package # hide from PAUSE
1637             Nice::Try::ScopeGuard;
1638              
1639             # older versions of perl have an issue with $@ during global destruction
1640 24 50   24   209 use constant UNSTABLE_DOLLARAT => ("$]" < '5.013002') ? 1 : 0;
  24         50  
  24         11900  
1641              
1642             sub _new
1643             {
1644 14     14   16418 my $this = shift( @_ );
1645 14   33     124 return( bless( [ @_ ] => ( ref( $this ) || $this ) ) );
1646             }
1647              
1648             sub DESTROY
1649             {
1650 14     14   832 my( $code, $args, $catch_err ) = @{ $_[0] };
  14         56  
1651             # save the current exception to make it available in the finally sub,
1652             # and to restore it after the eval
1653 14 50       44 my $err = defined( $catch_err ) ? $catch_err : $@;
1654 14         21 local $@ if( UNSTABLE_DOLLARAT );
1655 14 50       37 $@ = $catch_err if( defined( $catch_err ) );
1656             CORE::eval
1657             {
1658 14         22 $@ = $err;
1659 14         38 $code->( @$args );
1660 13         1392 1;
1661             }
1662             or do
1663 14 100       23 {
1664 1 50       21 CORE::warn
1665             "Execution of finally() block $code resulted in an exception, which "
1666             . '*CAN NOT BE PROPAGATED* due to fundamental limitations of Perl. '
1667             . 'Your program will continue as if this event never took place. '
1668             . "Original exception text follows:\n\n"
1669             . (defined $@ ? $@ : '$@ left undefined...')
1670             . "\n"
1671             ;
1672             };
1673             # maybe unnecessary?
1674 14         162 $@ = $err;
1675             }
1676             }
1677              
1678             {
1679             package
1680             Nice::Try::ObjectContext;
1681              
1682             sub new
1683             {
1684 2     2   24618 my $that = shift( @_ );
1685             # print( STDERR "Got here in Nice::Try::ObjectContext->new with args '", join( "', '", @_ ), "'\n" );
1686 2   33     21 return( bless( { val => [@_] } => ( ref( $that ) || $that ) ) );
1687             }
1688              
1689             sub callback
1690             {
1691 2     2   5 my $self = shift( @_ );
1692             # print( STDERR "Got here in Nice::Try::ObjectContext->dummy with args '", join( "', '", @_ ), "'\n" );
1693 2         12 return( $self->{val}->[0] );
1694             }
1695             }
1696              
1697             {
1698             package
1699             PPI::Element;
1700            
1701 24     24   209 no warnings 'redefine';
  24         58  
  24         3612  
1702             sub replace {
1703 70 50   70 1 217 my $self = ref $_[0] ? shift : return undef;
1704             # If our object and the other are not of the same class, PPI refuses to replace
1705             # to avoid damages to perl code
1706             # my $other = _INSTANCE(shift, ref $self) or return undef;
1707 70         108 my $other = shift;
1708             # die "The ->replace method has not yet been implemented";
1709 70         183 $self->parent->__replace_child( $self, $other );
1710 70         2576 1;
1711             }
1712             }
1713              
1714             1;
1715              
1716             # XXX POD
1717             __END__