File Coverage

blib/lib/Devel/Pragma.pm
Criterion Covered Total %
statement 53 55 96.3
branch 10 14 71.4
condition 2 3 66.6
subroutine 15 16 93.7
pod 3 5 60.0
total 83 93 89.2


line stmt bran cond sub pod time code
1             package Devel::Pragma;
2              
3 7     7   94470 use 5.008001;
  7         18  
4              
5             # make sure this is loaded first
6 7     7   2698 use Lexical::SealRequireHints;
  7         3638  
  7         36  
7              
8 7     7   155 use strict;
  7         26  
  7         117  
9 7     7   19 use warnings;
  7         8  
  7         150  
10              
11 7     7   22 use Carp qw(carp croak);
  7         7  
  7         372  
12 7     7   22 use Scalar::Util;
  7         7  
  7         240  
13 7     7   24 use XSLoader;
  7         8  
  7         137  
14              
15 7     7   18 use base qw(Exporter);
  7         7  
  7         1010  
16              
17             our $VERSION = '1.0.0';
18             our @EXPORT_OK = qw(my_hints hints new_scope ccstash scope fqname);
19             our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
20              
21             XSLoader::load(__PACKAGE__, $VERSION);
22              
23             # return a reference to the hints hash
24             sub my_hints() {
25             # set HINT_LOCALIZE_HH (0x20000)
26 57     57 0 11219 $^H |= 0x20000;
27 57         134 return \%^H;
28             }
29              
30 7     7   2182 BEGIN { *hints = \&my_hints }
31              
32             # make sure the "enable lexically-scoped %^H" flag is set (on by default in 5.10)
33             sub check_hints() {
34 45 100   45 0 78 unless ($^H & 0x20000) {
35 1         143 carp('Devel::Pragma: unexpected $^H (HINT_LOCALIZE_HH bit not set) - setting it now, but results may be unreliable');
36             }
37 45         52 return hints; # create it if it doesn't exist - in some perls, it starts out NULL
38             }
39              
40             # return a unique integer ID for the current scope
41             sub scope() {
42 29     29 1 1012 check_hints;
43 29         123 xs_scope();
44             }
45              
46             # return a boolean indicating whether this is the first time "use MyPragma" has been called in this scope
47             sub new_scope(;$) {
48 16   66 16 1 6100 my $caller = shift || caller;
49 16         24 my $hints = check_hints();
50              
51             # this is %^H as an integer - it changes as scopes are entered/exited i.e. it's a unique
52             # identifier for the currently-compiling scope (the scope in which new_scope
53             # is called)
54             #
55             # we don't need to stack/unstack it in %^H as %^H itself takes care of that
56             # note: we need to call this *after* %^H is referenced (and possibly autovivified) above
57             #
58             # every time new_scope is called, we write this scope ID to $^H{"Devel::Pragma::new_scope::$caller"}.
59             # if $^H{"Devel::Pragma::new_scope::$caller"} == scope() (i.e. the stored scope ID is the same as the
60             # current scope ID), then we're augmenting the current scope; otherwise we're in a new scope - i.e.
61             # a nested or outer scope that didn't previously "use MyPragma"
62              
63 16         19 my $current_scope = scope();
64 16         26 my $id = "Devel::Pragma::new_scope::$caller";
65 16 100       34 my $old_scope = exists($hints->{$id}) ? $hints->{$id} : 0;
66 16         8 my $new_scope; # is this a scope in which new_scope has not previously been called?
67              
68 16 100       22 if ($current_scope == $old_scope) {
69 9         7 $new_scope = 0;
70             } else {
71 7         16 $hints->{$id} = $current_scope;
72 7         7 $new_scope = 1;
73             }
74              
75 16         45 return $new_scope;
76             }
77              
78             # given a short name (e.g. "foo"), expand it into a fully-qualified name with the caller's package prefixed
79             # e.g. "main::foo"
80             #
81             # if the name is already fully-qualified, return it unchanged
82             sub fqname ($;$) {
83 5     5 1 109 my $name = shift;
84 5         3 my ($package, $subname);
85              
86 5         9 $name =~ s{'}{::}g;
87              
88 5 100       10 if ($name =~ /::/) {
89 3         9 ($package, $subname) = $name =~ m{^(.+)::(\w+)$};
90             } else {
91 2 50       6 my $caller = @_ ? shift : ccstash();
92 2         3 ($package, $subname) = ($caller, $name);
93             }
94              
95 5 50       12 return wantarray ? ($package, $subname) : "$package\::$subname";
96             }
97              
98             # helper function: return true if $ref ISA $class - works with non-references, unblessed references and objects
99             sub _isa($$) {
100 0     0   0 my ($ref, $class) = @_;
101 0 0       0 return Scalar::Util::blessed($ref) ? $ref->isa($class) : ref($ref) eq $class;
102             }
103              
104             # make sure "enable lexically-scoped %^H" is set in older perls, and export the requested functions
105             sub import {
106 11     11   1998 my $class = shift;
107 11         20 $^H |= 0x20000; # set HINT_LOCALIZE_HH (0x20000)
108 11         2287 $class->export_to_level(1, undef, @_);
109             }
110              
111             1;
112              
113             __END__