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   91037 use 5.008001;
  7         21  
4              
5             # make sure this is loaded first
6 7     7   3475 use Lexical::SealRequireHints;
  7         4229  
  7         38  
7              
8 7     7   167 use strict;
  7         13  
  7         127  
9 7     7   23 use warnings;
  7         9  
  7         183  
10              
11 7     7   23 use Carp qw(carp croak);
  7         8  
  7         431  
12 7     7   28 use Scalar::Util;
  7         7  
  7         267  
13 7     7   24 use XSLoader;
  7         10  
  7         136  
14              
15 7     7   23 use base qw(Exporter);
  7         11  
  7         1154  
16              
17             our $VERSION = '1.1.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 55     55 0 8332 $^H |= 0x20000;
27 55         151 return \%^H;
28             }
29              
30 7     7   2434 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 100 unless ($^H & 0x20000) {
35 1         146 carp('Devel::Pragma: unexpected $^H (HINT_LOCALIZE_HH bit not set) - setting it now, but results may be unreliable');
36             }
37 45         55 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 1336 check_hints;
43 29         129 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 11648 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         24 my $current_scope = scope();
64 16         30 my $id = "Devel::Pragma::new_scope::$caller";
65 16 100       47 my $old_scope = exists($hints->{$id}) ? $hints->{$id} : 0;
66 16         13 my $new_scope; # is this a scope in which new_scope has not previously been called?
67              
68 16 100       26 if ($current_scope == $old_scope) {
69 9         10 $new_scope = 0;
70             } else {
71 7         28 $hints->{$id} = $current_scope;
72 7         11 $new_scope = 1;
73             }
74              
75 16         66 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 96 my $name = shift;
84 5         4 my ($package, $subname);
85              
86 5         6 $name =~ s{'}{::}g;
87              
88 5 100       8 if ($name =~ /::/) {
89 3         8 ($package, $subname) = $name =~ m{^(.+)::(\w+)$};
90             } else {
91 2 50       10 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 9     9   15762 my $class = shift;
107 9         17 $^H |= 0x20000; # set HINT_LOCALIZE_HH (0x20000)
108 9         2038 $class->export_to_level(1, undef, @_);
109             }
110              
111             1;
112              
113             __END__