File Coverage

blib/lib/Devel/MAT/Tool/Future.pm
Criterion Covered Total %
statement 126 137 91.9
branch 55 78 70.5
condition 11 15 73.3
subroutine 23 26 88.4
pod 4 10 40.0
total 219 266 82.3


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2014-2022 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Tool::Future 0.01;
7              
8 3     3   20364055 use v5.14;
  3         8  
9 3     3   13 use warnings;
  3         4  
  3         91  
10 3     3   12 use base qw( Devel::MAT::Tool );
  3         3  
  3         613  
11             Devel::MAT::Tool->VERSION( '0.48' );
12              
13 3     3   4385 use Carp;
  3         5  
  3         173  
14              
15 3     3   15 use Syntax::Keyword::Match;
  3         5  
  3         20  
16 3     3   1501 use String::Tagged;
  3         16647  
  3         81  
17              
18 3     3   17 use constant FOR_UI => 1;
  3         6  
  3         147  
19              
20 3     3   1100 use File::ShareDir qw( module_file );
  3         59746  
  3         3314  
21              
22             =head1 NAME
23              
24             C - analyse C logic
25              
26             =head1 DESCRIPTION
27              
28             This C tool performs analysis of objects and code logic built
29             using the L module.
30              
31             This version supports analysing code based on C version 0.24.
32              
33             =cut
34              
35             sub AUTOLOAD_TOOL
36             {
37 2     2 1 79746 shift;
38 2         4 my ( $pmat ) = @_;
39 2 50       24 return 1 if eval { $pmat->find_symbol( '%Future::' ) };
  2         10  
40             }
41              
42             sub init_tool
43             {
44 2     2 1 260 my $self = shift;
45              
46 2         9 my $df = $self->df;
47              
48 2         16 my $heap_total = scalar $df->heap;
49 2         13 my $count;
50              
51             # Find all the classes that derive from Future
52 2         8 $self->{classes} = \my %classes;
53 2         5 $classes{Future}++;
54              
55 2         2 $count = 0;
56 2         5 foreach my $sv ( $df->heap ) {
57 151500         576063 $count++;
58 151500 100       183065 $self->report_progress( sprintf "Finding Future subclasses in %d of %d (%.2f%%)",
59             $count, $heap_total, 100*$count / $heap_total ) if ($count % 1000) == 0;
60              
61 151500 100       240125 next unless $sv->type eq "STASH";
62              
63             # populate the %classes hash
64 639         996 $self->class_is_future( $sv );
65             }
66              
67 2         10760 $count = 0;
68 2         28 foreach my $sv ( $df->heap ) {
69 151500         1011280 $count++;
70 151500 100       184619 $self->report_progress( sprintf "Finding Future instances in %d of %d (%.2f%%)",
71             $count, $heap_total, 100*$count / $heap_total ) if ($count % 1000) == 0;
72              
73 151500 100       234266 next unless my $pkg = $sv->blessed;
74              
75 621 100       8079 $classes{ $pkg->stashname } and $sv->{tool_future}++;
76             }
77              
78 2         11147 $self->init_cmd;
79             }
80              
81             sub init_cmd
82             {
83 2     2 0 14 my $self = shift;
84              
85             Devel::MAT::Tool::Show->register_extra(
86             sub {
87 4     4   3829 my ( $sv ) = @_;
88              
89 4 50       10 $sv->is_future or return undef;
90              
91 4         10 my $state = $sv->future_state;
92              
93 4         12 Devel::MAT::Cmd->printf( " %s state %s\n",
94             Devel::MAT::Cmd->format_symbol( "Future" ),
95             $state,
96             );
97              
98             match( $state : eq ) {
99             case( "done" ) {
100 1         6 my @result = $sv->future_result;
101 1         62 my @str;
102 1 50       4 push @str, "(empty)" if !@result;
103 1 50       9 push @str, Devel::MAT::Cmd->format_sv_with_value( $result[0] ) if @result;
104 1 50       93 push @str, "..." if @result > 1;
105              
106 1         3 Devel::MAT::Cmd->printf( " %s result: %s\n",
107             Devel::MAT::Cmd->format_symbol( "Future" ),
108             String::Tagged->join( ", ", @str ),
109             );
110             }
111 4 100       40 case( "failed" ) {
    100          
112 1         6 my @failure = $sv->future_failure;
113 1         38 my @str;
114 1 50       5 push @str, "(empty)" if !@failure;
115 1 50       3 if( @failure ) {
116 1 50       7 push @str, defined $failure[0]->pv
117             ? Devel::MAT::Cmd->format_value( $failure[0]->pv, pv => 1 )
118             : Devel::MAT::Cmd->format_sv( $failure[0] );
119 1 50       22 push @str, "..." if @failure > 1;
120             }
121              
122 1         3 Devel::MAT::Cmd->printf( " %s failure: %s\n",
123             Devel::MAT::Cmd->format_symbol( "Future" ),
124             String::Tagged->join( ", ", @str ),
125             );
126             }
127             }
128             }
129 2         44 );
130             }
131              
132             sub init_ui
133             {
134 0     0 1 0 my $self = shift;
135 0         0 my ( $ui ) = @_;
136              
137 0         0 foreach (qw( pending done failed cancelled )) {
138 0         0 $ui->register_icon( name => "future-$_", svg => module_file( __PACKAGE__, "icons/future-$_.svg" ) );
139             }
140              
141             $ui->provides_sv_detail(
142             type => "widget",
143             title => "Future",
144 0     0   0 render => sub { $self->render_sv_detail( @_ ) },
145 0         0 );
146             }
147              
148             =head1 METHODS
149              
150             =cut
151              
152             =head2 $ok = $tool->class_is_future( $pkg )
153              
154             Returns true if the given package is a C class. C<$pkg> may be either
155             a C instance referring to a stash, or a plain string.
156              
157             =cut
158              
159             # TODO: This kind of logic might belong in Devel::MAT::SV itself
160              
161             sub class_is_future
162             {
163 912     912 1 1454 my $self = shift;
164 912         1142 my ( $pkg ) = @_;
165 912 100       1988 ref $pkg or $pkg = $self->{pmat}->find_symbol( "%${pkg}::" ); # stash
166              
167 912   100     30909 return $self->{classes}{$pkg->stashname} //= $self->_class_is_future( $pkg );
168             }
169              
170             sub _class_is_future
171             {
172 637     637   3837 my $self = shift;
173 637         724 my ( $pkg ) = @_;
174              
175 637 50       832 return 1 if $pkg->stashname eq "Future";
176              
177 637 100       2639 my $isagv = $pkg->value( "ISA" ) or return 0;
178 261 50       3885 my $isaav = $isagv->array or return 0;
179              
180 261         3206 foreach my $superclass ( $isaav->elems ) {
181 269 100       4726 return 1 if $self->class_is_future( $superclass->pv );
182             }
183              
184 260         1721 return 0;
185             }
186              
187             =head1 SV METHODS
188              
189             This tool adds the following SV methods.
190              
191             =cut
192              
193             =head2 is_future (SV)
194              
195             $ok = $sv->is_future
196              
197             Returns true if the C instance represents a C
198             instance.
199              
200             =cut
201              
202             sub Devel::MAT::SV::is_future
203             {
204 303126     303126 0 287216 my $sv = shift;
205              
206 303126         615427 return defined $sv->{tool_future};
207             }
208              
209             =head2 future_state (SV)
210              
211             $state = $sv->future_state
212              
213             Returns a string describing the state of the given C instance; one of
214             C, C, C or C.
215              
216             =cut
217              
218             sub Devel::MAT::SV::future_state
219             {
220 24     24 0 1644 my $sv = shift;
221              
222 24 50       51 $sv->is_future or croak "$sv is not a Future";
223              
224 24         35 my $tmp;
225 24 100 66     119 if( $tmp = $sv->value( "cancelled" ) and $tmp->uv ) {
    100 66        
    100          
226 6         162 return "cancelled";
227             }
228             elsif( $tmp = $sv->value( "failure" ) ) {
229 6         261 return "failed";
230             }
231             elsif( $tmp = $sv->value( "ready" ) and $tmp->uv ) {
232 6         250 return "done";
233             }
234             else {
235 6         287 return "pending";
236             }
237             }
238              
239             =head2 future_result
240              
241             @result = $sv->future_result
242              
243             Returns a list of SVs containing the result of a successful C.
244              
245             =cut
246              
247             sub Devel::MAT::SV::future_result
248             {
249 2     2 0 4 my $sv = shift;
250              
251 2 50       4 $sv->is_future or croak "$sv is not a Future";
252              
253 2         8 return $sv->value( "result" )->rv->elems;
254             }
255              
256             =head2 future_failure
257              
258             @failure = $sv->future_failure
259              
260             Returns a list of SVs containing the failure of a failed C.
261              
262             =cut
263              
264             sub Devel::MAT::SV::future_failure
265             {
266 2     2 0 4 my $sv = shift;
267              
268 2 50       4 $sv->is_future or croak "$sv is not a Future";
269              
270 2         6 return $sv->value( "failure" )->rv->elems;
271             }
272              
273             sub render_sv_detail
274             {
275 0     0 0 0 my $self = shift;
276 0         0 my ( $sv ) = @_;
277              
278 0 0       0 $self->is_future( $sv ) or return undef;
279              
280 0         0 my $state = $self->future_state( $sv );
281              
282 0         0 return Devel::MAT::UI->make_table(
283             State => Devel::MAT::UI->make_widget_text_icon( ucfirst $state, "future-$state" ),
284             );
285             }
286              
287             =head1 EXTENSIONS TO FIND
288              
289             =cut
290              
291             package # hide
292             Devel::MAT::Tool::Find::filter::future;
293 3     3   28 use base qw( Devel::MAT::Tool::Find::filter );
  3         6  
  3         893  
294              
295             =head2 find future
296              
297             pmat> find future -f
298             HASH(2)=Future at 0x55d43c854660: Future(failed) - SCALAR(PV) at 0x55d43c8546f0 = "It failed"
299              
300             Lists SVs that are Future instances, optionally matching only futures in a
301             given state.
302              
303             Takes the following named options
304              
305             =over 4
306              
307             =item --pending, -p
308              
309             Lists only Futures in the pending state
310              
311             =item --done, -d
312              
313             Lists only Futures in the done state
314              
315             =item --failed, -f
316              
317             Lists only Futures in the failed state
318              
319             =item --cancelled, -c
320              
321             Lists only Futures in the cancelled state
322              
323             =back
324              
325             =cut
326              
327 3     3   20 use constant FILTER_DESC => "Future instances";
  3         7  
  3         239  
328              
329 3         1062 use constant FILTER_OPTS => (
330             pending => { help => "only pending futures",
331             alias => "p" },
332             done => { help => "only done futures",
333             alias => "d" },
334             failed => { help => "only failed futures",
335             alias => "f" },
336             cancelled => { help => "only cancelled futures",
337             alias => "c" },
338 3     3   18 );
  3         5  
339              
340             sub build
341             {
342 4     4   4125 my $self = shift;
343 4         6 my $inv = shift;
344 4         6 my %opts = %{ +shift };
  4         12  
345              
346 4         8 my %only;
347 4   66     25 $opts{$_} and $only{$_}++ for qw( pending done failed cancelled );
348              
349             return sub {
350 303092     303092   1432058 my ( $sv ) = @_;
351              
352 303092 100       333655 return unless $sv->is_future;
353              
354 16         46 my $state = $sv->future_state;
355              
356 16 100 66     86 return if %only and !$only{$state};
357              
358 4         30 my $ret = String::Tagged->from_sprintf( "%s(%s)",
359             Devel::MAT::Cmd->format_symbol( "Future" ), # TODO: full class name of this instance?
360             Devel::MAT::Cmd->format_note( $state, 1 ),
361             );
362              
363             match( $state : eq ) {
364             case( "done" ) {
365 1         5 my @result = $sv->future_result;
366 1 50       70 $ret .= " - (empty)" if !@result;
367 1 50       7 $ret .= " - " . Devel::MAT::Cmd->format_sv_with_value( $result[0] ) if @result;
368 1 50       113 $ret .= ", ..." if @result > 1;
369             }
370 4 100       816 case( "failed" ) {
    100          
371 1         6 my @failure = $sv->future_failure;
372 1 50       51 $ret .= " - (empty)" if !@failure;
373 1 50       4 if( @failure ) {
374 1 50       11 $ret .= " - " . ( defined $failure[0]->pv
375             ? Devel::MAT::Cmd->format_value( $failure[0]->pv, pv => 1 )
376             : Devel::MAT::Cmd->format_sv( $failure[0] ) );
377 1 50       45 $ret .= ", ..." if @failure > 1;
378             }
379             }
380             }
381              
382 4         19 return $ret;
383             }
384 4         28 }
385              
386             =head1 AUTHOR
387              
388             Paul Evans
389              
390             =cut
391              
392             0x55AA;