File Coverage

blib/lib/ensure.pm
Criterion Covered Total %
statement 98 152 64.4
branch 31 78 39.7
condition 24 70 34.2
subroutine 16 19 84.2
pod 0 9 0.0
total 169 328 51.5


line stmt bran cond sub pod time code
1             #=========================================================================================
2             package ensure ; # a pragma
3             #=========================================================================================
4              
5 1     1   21425 use 5.006 ; use v5.8.8 ; # Not tested for anything less
  1     1   6  
  1         48  
  1         12  
  1         3  
  1         50  
6              
7 1     1   6 use strict ;
  1         8  
  1         60  
8 1     1   6 use warnings ;
  1         2  
  1         76  
9              
10             our $VERSION = '1.10' ; # 14-Oct-2008
11              
12             #=========================================================================================
13              
14 1     1   5 use Exporter () ; # Exporter::import used explicitly
  1         2  
  1         1112  
15              
16             our @EXPORT = qw(import) ;
17             our @EXPORT_OK = qw(register) ;
18              
19             #=========================================================================================
20             # We work on the basis that in all entries in the symbol table have a defined {SCALAR}
21             # part -- whether there is a scalar or not.
22              
23             test_scalar_symbol() or die "*** symbol table form changed -- package 'ensure' broken\n" ;
24              
25             sub test_scalar_symbol {
26 1     1 0 1 return defined(*{stash(__PACKAGE__)->{'test_scalar_symbol'}}{SCALAR}) ;
  1         3  
27             } ;
28              
29             #=========================================================================================
30             # Tables of packages and variables known to ensure
31              
32             my %packages = () ; # Keys = packages registered for ensure CHECK processing
33             # Values = ref:Stash for registered packages
34              
35             my %exporters = () ; # Keys = packages which we've seen export stuff
36             # Values = true => package includes IMPLICIT tag
37              
38             my %no_ensure = () ; # Keys = packages with things declared no_ensure
39             # Values = [name, name, ...]
40              
41             my %no_scalar = () ; # Keys = address of undefined scalar declared 'no ensure'
42             # Values = exporting package
43              
44             my $ensure_errors = 0 ; # Count of errors. Dies at end of ensure CHECK if != 0.
45              
46             #=========================================================================================
47             # Manual registration
48              
49             register(__PACKAGE__) ; # Register ourselves
50              
51             sub register { # May be used, eg, to register 'main'
52 2     2 0 3 my ($p) = @_ ;
53 2   33     10 $packages{$p} ||= stash($p) ;
54             } ;
55              
56             #=========================================================================================
57             # Two small utilities
58              
59             sub err { # Issue warning message and increment $ensure_errors
60 0     0 0 0 warn '+++ ensure: ', @_, "\n" ;
61 0         0 return $ensure_errors++ ;
62             } ;
63              
64             sub crunch { # Die
65 0     0 0 0 die '*** ensure: ', @_, "\n" ;
66             } ;
67              
68             sub suq { # Sort given list and ensure all entries are unique
69 0     0 0 0 my %l = map { ($_, undef) } @_ ;
  0         0  
70 0         0 return sort keys %l ;
71             } ;
72              
73             #=========================================================================================
74             # ensure::import
75             # ==============
76             #
77             # This will be invoked:
78             #
79             # a. when a package does 'use ensure', which:
80             #
81             # - registers the package for the ensure CHECK block checks.
82             #
83             # - imports into the package the ensure::import function.
84             #
85             # b. when a package which has done 'use ensure' is itself used:
86             #
87             # - the first time this happens, the package's exports are checked.
88             #
89             # - in all cases the import list extensions (:ALL :NONE :IMPLICIT) are
90             # implemented, before jumping to the standard Exporter::import.
91             #
92             # Requires: $ep -- package which is being imported from ) passed to...
93             # @imports -- import list, from "use Fred (@import) ;" ) ...Exporter::import
94             #
95             # Returns: nothing
96              
97             sub import {
98 1     1   11 my $ep = $_[0] ;
99              
100             # If we are running the import on behalf of ourselves, we register importing package.
101              
102 1         2 my $ip = '' ;
103 1 50       4 if ($ep eq __PACKAGE__) { register($ip = scalar(caller)) ; } ;
  1         6  
104              
105             # If this is the first time we have seen this package export stuff, we run checks
106             # across the export declarations.
107              
108 1 50       8 my $implicit = exists($exporters{$ep}) ? $exporters{$ep} : check_exports($ep) ;
109              
110             # Now we deal with the import list, if it is not empty
111              
112 1 50       5 if (scalar(@_) > 1) {
113              
114 0 0 0     0 if ($_[1] eq ':ALL') {
    0 0        
    0 0        
      0        
115              
116             # Importing ':ALL' -- replace ':ALL' by contents of @EXPORT and @EXPORT_OK
117              
118 0         0 my $st = $packages{$ep} ;
119 0 0       0 splice( @_, 1, 1, suq(@{stash_value($st, '@EXPORT' ) || []},
  0 0       0  
120 0         0 @{stash_value($st, '@EXPORT_OK') || []}) ) ;
121             }
122             elsif ( ($_[1] eq ':NONE') || (!$implicit && ($_[1] eq ':IMPLICIT')) ) {
123              
124             # Importing ':NONE' or ':IMPLICIT' when no IMPLICIT tag exists.
125            
126 0         0 my $i = 2 ;
127 0   0     0 while (defined($_[$i]) && ($_[$i] =~ m/^!/)) { $i++ ; } ;
  0         0  
128              
129 0         0 splice(@_, 1, $i-1) ; # Drop :NONE/:IMPLICIT and following '!'
130              
131             # Give up now if nothing left of list
132              
133 0 0       0 if (scalar(@_) == 1) { return ; } ; # Give up now if nothing left of list
  0         0  
134             }
135             elsif ( $implicit && ($_[1] ne ':IMPLICIT') && ($_[1] !~ m/^!/) ) {
136              
137             # Exporting package has 'IMPLICIT' tag and import list a) is not empty,
138             # and b) does not start ':IMPLICIT'
139             # and c) does not start '!...'
140              
141 0         0 splice(@_, 1, 0, ':IMPLICIT') ;
142             } ;
143             } ;
144              
145             # Now we can proceed to standard import !
146              
147 1 50 33     8 if (($ip eq 'main') && (scalar(@_) == 1))
148 1         12 { return ; } ; # (Unless importing self to main, in default fashion.)
149              
150 0         0 goto &Exporter::import ; # As if called in the first place
151             } ;
152              
153             #=========================================================================================
154             # check_exports: run checks across exports & establish whether has 'IMPLICIT' tag.
155             #
156             # Checks that:
157             #
158             # a) everything in @EXPORT & @EXPORT_OK is defined, except where declared 'no ensure'
159             #
160             # b) everything in %EXPORT_TAGS (other than 'IMPLICIT') must appear in @EXPORT or
161             # @EXPORT_OK.
162             #
163             # c) everything in any 'IMPLICIT' tag must appear in @EXPORT.
164             #
165             # d) everything in @EXPORT_FAIL must appear in @EXPORT or @EXPORT_OK.
166             #
167             # Sets $exporters{$ep} = true iff there is an 'IMPLICIT' tag, false otherwise.
168             #
169             # NB: to be called the first time the package is seen exporting stuff.
170             #
171             # Requires: $ep -- name of package which is exporting stuff -- default is caller !
172             #
173             # Returns: true => exporting package has an 'IMPLICIT' tag
174              
175             sub check_exports {
176 1     1 0 2 my ($ep) = @_ ;
177              
178             #----------------------------------------------------------------------------------
179             # Get the stash for the exporting package -- must be registered already !!
180              
181 1 50       4 my $st = $packages{$ep}
182             or crunch "check_exports: package $ep not registered" ;
183              
184             #----------------------------------------------------------------------------------
185             # a) check contents of @EXPORT & @EXPORT_OK, given any 'no ensure' declarations
186             #
187             # Names in @EXPORT & @EXPORT_OK are checked thus:
188             #
189             # * name -- requires: glob{CODE}, SCALAR or REF
190             # * $name -- requires: glob{SCALAR} to have a defined value
191             # * @name -- requires: glob{ARRAY}
192             # * %name -- requires: glob{HASH}
193             # * &name -- requires: glob{CODE}
194             # * *name -- requires: the name to exist as glob
195             #
196             # Note that the undecorated name works for 5.10.0 and onwards constant values.
197             #
198             # Note that for $name this means that it must have some value other than 'undef'.
199             # (This is because it is not possible to distinguish no $name declaration at all
200             # from a declaration which leaves the value undefined.)
201             #
202             # Names declared 'no ensure' *must* fail the above.
203              
204 1   50     3 my $exp = stash_value($st, '@EXPORT' ) || [] ;
205 1   50     3 my $eok = stash_value($st, '@EXPORT_OK') || [] ;
206 1   50     6 my $nen = $no_ensure{$ep} || [] ;
207              
208             # Collect all exports (from EXPORT and EXPORT_OK) & all 'no ensure' names.
209              
210 1         2 my %all_exports = map { ($_, 1) } @$exp, @$eok ;
  2         7  
211 1         2 my %undefined = map { ($_, 1) } @$nen ;
  0         0  
212              
213 1         8 foreach my $name (sort keys %all_exports) {
214 2         5 my ($id, $t) = undecorate($name) ;
215              
216 2         7 my $rv = $st->{$id} ;
217 2         4 my $def = defined($rv) ;
218 2 50       5 if ($def) {
219 2 50       5 if (!ref($rv)) {
220 2 50       8 if ($t eq 'SCALAR') { $def = defined(${$rv = *$rv{$t}}) ; }
  0 50       0  
  0         0  
221 2   50     13 elsif ($t ne 'GLOB') { $def = defined(*$rv{$t || 'CODE'}) ; } ;
222             }
223             else {
224 0   0     0 $def = ($t eq '') || ($t eq 'CODE') ;
225             } ;
226             } ;
227              
228 2 50 33     25 if (exists($undefined{$name}) || (($t eq 'CODE') && exists($undefined{$id}))
      33        
      33        
      33        
229             || (($t eq '') && exists($undefined{'&'.$id}))) {
230 0 0       0 if ($def) {
    0          
231 0         0 err "'$name' in '$ep\' is declared 'no ensure', but is defined" ;
232             }
233             elsif ($t eq 'SCALAR') {
234 1 0   1   7 if (!defined($rv)) { no strict 'refs' ; $rv = \${"$ep\:\:$id"} ; } ;
  1         2  
  1         1085  
  0         0  
  0         0  
  0         0  
235 0         0 $no_scalar{"$rv"} = $ep ;
236             } ;
237             }
238             else {
239 2 50       8 if (!$def) {
240 0         0 err "'$name' is exported by '$ep', but is not defined" ;
241             } ;
242             } ;
243             } ;
244              
245             #----------------------------------------------------------------------------------
246             # b) check that everything in the %EXPORT_TAGS is in @EXPORT or @EXPORT_OK
247             # (except for any IMPLICIT tag).
248             # c) check that everything in any %EXPORT_TAGS{IMPLICIT} is in @EXPORT
249              
250 1         3 my $implicit = 0 ;
251              
252 1 50       3 if (my $etg = stash_value($st, '%EXPORT_TAGS')) {
253 0         0 foreach my $tag (sort keys %$etg) {
254 0 0       0 if ($tag ne 'IMPLICIT') {
255 0         0 foreach my $name (suq @{$$etg{$tag}}) {
  0         0  
256 0 0       0 if (!exists($all_exports{$name})) {
257 0         0 err "'$name' is in '$ep\'s '$tag' tag list,",
258             " but not in \@EXPORT or \@EXPORT_OK" ;
259             } ;
260             } ;
261             }
262             else {
263 0         0 $implicit = 1 ;
264 0         0 my %default = map { ($_, 1) } @$exp ; # That which is in @EXPORT
  0         0  
265 0         0 foreach my $name (suq @{$$etg{IMPLICIT}}) {
  0         0  
266 0 0       0 if (!exists($default{$name})) {
267 0         0 err "'$name' is in '$ep\'s 'IMPLICIT' tag list, but not in \@EXPORT" ;
268             } ;
269             } ;
270             } ;
271             } ;
272             } ;
273              
274             #----------------------------------------------------------------------------------
275             # d) check that everything in the @EXPORT_FAIL is in @EXPORT or @EXPORT_OK
276              
277 1 50       4 if (my $ef = stash_value($st, '@EXPORT_FAIL')) {
278 0         0 foreach my $name (suq(@$ef)) {
279 0 0       0 if (!exists($all_exports{$name})) {
280 0         0 err "'$name' is in '$ep\'s \@EXPORT_FAIL, but not in \@EXPORT or \@EXPORT_OK" ;
281             } ;
282             } ;
283             } ;
284              
285             #----------------------------------------------------------------------------------
286             # Done -- record exporting package and whether it has an 'IMPLICIT' tag
287              
288 1         5 return $exporters{$ep} = $implicit ;
289             } ;
290              
291             #=========================================================================================
292             # ensure::unimport
293             # ================
294             #
295             # unimport: mechanics for no ensure qw(....) ;
296             #
297             # no ensure qw(name $name @name %name &name *name)
298             #
299             # The export checks use the full name, complete with decoration. So if you want to
300             # export an undefined '@name' (for example) you need to be specific.
301             #
302             # The CHECK block ignores the decoration. You can say, for example, that you expect
303             # '$name' to be undefined, the effect is that it is deemed OK if nothing at all is
304             # defined for 'name'.
305             #
306             # Requires: $self = ourselves (__PACKAGE__) !
307             # list of possibly decorated names
308             #
309             # Returns: nothing
310              
311             sub unimport {
312 1     1   3344 shift(@_) ; # Discard self
313 1   50     4 push @{$no_ensure{scalar(caller)} ||= []}, @_ ;
  1         11  
314 1         581 return 1 ;
315             } ;
316              
317             #=========================================================================================
318             # Post Compile-Time Checks -- the ensure CHECK block
319             # ==================================================
320             #
321             # For all packages that have been registered, we look for any completely undefined
322             # simple names -- which we treat as undefined subroutine errors, unless declared
323             # 'no ensure'.
324             #
325             # By simple we mean names starting '_' or alphabetic, excluding a small number of
326             # well known names.
327              
328             CHECK {
329              
330             # These may appear undefined in the stash or are otherwise not worth checking.
331              
332             # a and b appear if sort is used ?
333             # MODIFY_xxx_ATTRIBUTES appear and are undefined if a variable is declared ': shared'.
334              
335 1     1   6 my %except = map { ($_, 1) } (qw(a b BEGIN UNITCHECK CHECK INIT END
  12         30  
336             DESTROY AUTOLOAD
337             MODIFY_SCALAR_ATTRIBUTES
338             MODIFY_ARRAY_ATTRIBUTES
339             MODIFY_HASH_ATTRIBUTES)) ;
340              
341             # Run checks across all registered packages
342              
343 1         13 foreach my $pkg (sort keys(%packages)) {
344              
345             # Debug introduces a number of undefined things in 'main', which just get in the way
346              
347 2 100 33     25 if (($^D || $^P) && ($pkg eq 'main')) {
      66        
348 1         14 print STDERR "+++ NB: Debug prevents ensure check for '$pkg'\n" ;
349 1         3 next ;
350             } ;
351              
352             # Collect any 'no ensure' names
353              
354 1         3 my %undefined = () ;
355 1 50       4 if (exists($no_ensure{$pkg})) {
356 0         0 %undefined = map { s/^[\$@%&*]// ; ($_, 1) } @{$no_ensure{$pkg}} ;
  0         0  
  0         0  
  0         0  
357             } ;
358              
359             # Check the stash for this package
360              
361 1         3 my $stash = $packages{$pkg} ; # Stash for package
362              
363 1         11 NAME: foreach my $name (sort keys %$stash) {
364 16 100 66     181 if (($name =~ m/^(?:__|_?\W|_?\d)/) # Ignore names which are not simple...
      66        
365             || $except{$name} # ...or which are exceptional
366             || $undefined{$name}) # ...or which are declared 'no ensure'
367 2         5 { next NAME ; } ;
368              
369 14         32 my $rv = $stash->{$name} ; # Get the stash entry
370              
371 14 50       27 next if !defined($rv); # Ignore undefined stash entries
372              
373             # OK if stash entry is ref() (=> is 5.10.0 or later 'constant')
374             # or if have a {CODE} value -- these are the commonest cases !
375              
376 14 100 66     56 if (ref($rv) || defined(*$rv{CODE})) { next NAME ; } ;
  11         30  
377              
378             # OK if glob has a defined {SCALAR} value
379             # or if undefined {SCALAR} is import of an exported 'no ensure'
380              
381 3         7 my $rs = *$rv{SCALAR} ;
382 3 100 66     19 if (defined($rs) && (defined($$rs) || exists($no_scalar{"$rs"}))) { next NAME ; } ;
  1   33     3  
383              
384             # OK if glob has at least one of these other types of value.
385              
386 2         3 foreach my $type (qw(ARRAY HASH IO FORMAT)) {
387 2 50       23 if (defined(*$rv{$type})) { next NAME ; } ;
  2         6  
388             } ;
389              
390             # Generate error for name with no defined value
391              
392 0         0 err "$pkg\:\:$name is undefined" ;
393             } ;
394             } ;
395              
396             # Now... if any errors seen by ensure, give up !
397              
398 1 50       943 if ($ensure_errors) { crunch "$ensure_errors errors found" ; } ;
  0         0  
399             } ;
400              
401             #=========================================================================================
402             # Stash Access
403              
404             #-----------------------------------------------------------------------------------------
405             # stash: get ref:Stash for given package
406             #
407             # Requires: $pkg -- package name -- no trailing '::' -- ASSUMED VALID
408             #
409             # Returns: ref:Stash -- i.e. hash containing symbols for given package
410              
411             sub stash {
412 3     3 0 4 my ($pkg) = @_ ;
413 1     1   6 no strict qw(refs) ;
  1         2  
  1         334  
414 3 0       3 return *{$pkg.'::'}{HASH} or crunch "cannot find package '$pkg'" ;
  3         23  
415             } ;
416              
417             #-----------------------------------------------------------------------------------------
418             # stash_value: get value of SCALAR, ARRAY or HASH from given package/stash
419             #
420             # Requires: $st -- ref:Stash (as returned by stash())
421             # $name -- decorated name of value
422             #
423             # Returns: value -- if SCALAR and scalar is defined
424             # ref:Value -- if ARRAY, HASH and value is defined
425             # undef -- name not found or value not defined
426              
427             sub stash_value {
428 4     4 0 6 my ($st, $name) = @_ ;
429              
430 4         8 my ($id, $type) = undecorate($name) ;
431 4         9 my $rv = $st->{$id} ;
432 4 100       10 if (defined($rv)) {
433 2 50       4 if (!ref($rv)) {
434 2         4 $rv = *$rv{$type} ;
435 2 50 33     15 if (defined($rv) && ($type eq 'SCALAR')) { $rv = $$rv ; } ;
  0         0  
436             }
437             else {
438 0         0 $rv = undef ; # ref:SCALAR or ref:REF => 5.10.0 type constant
439             } ;
440             } ;
441              
442 4         16 return $rv ;
443             } ;
444              
445             #-----------------------------------------------------------------------------------------
446             # undecorate: remove decoration from name and return explicit type, if any
447             #
448             # Requires: $name -- possibly decorated name
449             #
450             # Returns: ($id, $type) -- $id = name less any decoration
451             # $type = if decorated: SCALAR, ARRAY, HASH, CODE or GLOB
452             # otherwise: ''
453              
454             my %TYPE = qw($ SCALAR @ ARRAY % HASH & CODE * GLOB) ;
455              
456             sub undecorate {
457 6     6 0 9 my ($id) = @_ ;
458 6         8 my $type = '' ;
459 6 100       25 if ($id =~ s/^([\$@%&*])//) { $type = $TYPE{$1} ; } ;
  4         10  
460 6         16 return ($id, $type) ;
461             } ;
462              
463             #_________________________________________________________________________________________
464             1 ; # OK -- end of ensure
465              
466             __END__