File Coverage

blib/lib/Devel/GlobalPhase.pm
Criterion Covered Total %
statement 23 24 95.8
branch 7 12 58.3
condition 3 6 50.0
subroutine 7 7 100.0
pod 1 2 50.0
total 41 51 80.3


line stmt bran cond sub pod time code
1             package Devel::GlobalPhase;
2 5     5   12271 use strict;
  5         9  
  5         181  
3 5     5   28 use warnings;
  5         10  
  5         339  
4              
5             our $VERSION = '0.002004';
6             $VERSION = eval $VERSION;
7              
8 5     5   25 use base 'Exporter';
  5         10  
  5         1766  
9              
10             our @EXPORT = qw(global_phase);
11              
12             sub global_phase ();
13             sub tie_global_phase;
14              
15             sub import {
16 4     4   27 my $class = shift;
17 4         6 my $var;
18             my @imports = map {
19 4 50 33     10 ($_ && $_ eq '-var') ? do {
  1         12  
20 1         2 $var = 1;
21 1         3 ();
22             } : $_;
23             } @_;
24 4 100 66     46 if (@imports || !$var) {
25 3         282 Exporter::export_to_level($class, 1, @imports);
26             }
27 4 100       66 if ($var) {
28 1         38 tie_global_phase;
29             }
30             }
31              
32             BEGIN {
33 5 50   5   31 if (defined ${^GLOBAL_PHASE}) {
34 5 50   8 1 532 eval <<'END_CODE' or die $@;
  8     1 0 2502  
  1         48  
35              
36             sub global_phase () {
37             return ${^GLOBAL_PHASE};
38             }
39              
40             sub tie_global_phase { 1 }
41              
42             1;
43              
44             END_CODE
45             }
46             else {
47 0 0       0 eval <<'END_CODE' or die $@;
48              
49             use B ();
50              
51             my $global_phase = 'START';
52             if (B::main_start()->isa('B::NULL')) {
53             # loaded during initial compile
54             eval <<'END_EVAL' or die $@;
55              
56             CHECK { $global_phase = 'CHECK' }
57             # try to install an END block as late as possible so it will run first.
58             INIT { eval q( END { $global_phase = 'END' } ) }
59             # INIT is FIFO so we can force our sub to be first
60             unshift @{ B::init_av()->object_2svref }, sub { $global_phase = 'INIT' };
61              
62             1;
63              
64             END_EVAL
65             }
66             else {
67             # loaded during runtime
68             $global_phase = 'RUN';
69             }
70             END { $global_phase = 'END' }
71              
72             sub global_phase () {
73             if ($global_phase eq 'DESTRUCT') {
74             # no need for extra checks at this point
75             }
76             elsif ($global_phase eq 'START') {
77             # we use a CHECK block to set this as well, but we can't force
78             # ours to run before other CHECKS
79             if (!B::main_root()->isa('B::NULL') && B::main_cv()->DEPTH == 0) {
80             $global_phase = 'CHECK';
81             }
82             }
83             elsif (${B::main_cv()} == 0) {
84             $global_phase = 'DESTRUCT';
85             }
86             elsif ($global_phase eq 'INIT' && B::main_cv()->DEPTH > 0) {
87             $global_phase = 'RUN';
88             }
89              
90             if ($global_phase eq 'RUN' && $^S) {
91             # END blocks are FILO so we can't install one to run first.
92             # only way to detect END reliably seems to be by using caller.
93             # I hate this but it seems to be the best available option.
94             # The top two frames will be an eval and the END block.
95             my $i = 0;
96             $i++ while CORE::caller($i + 1);
97             if ($i < 1) {
98             # there should always be the sub call and an eval frame ($^S is true).
99             # this will only happen if we're in END, but the outer frames are broken.
100             $global_phase = 'END';
101             }
102             elsif ($i > 1) {
103             # If we're ENDing due to an exit or die in a sub generated in an eval,
104             # these caller calls can cause a segfault. I can't find a way to detect
105             # this.
106             my @top = CORE::caller($i);
107             my @next = CORE::caller($i - 1);
108             if (
109             $top[3] eq '(eval)'
110             && $next[3] =~ /::END$/
111             && $top[2] == $next[2]
112             && $top[1] eq $next[1]
113             && $top[0] eq 'main'
114             && $next[0] eq 'main'
115             ) {
116             $global_phase = 'END';
117             }
118             }
119             }
120              
121             return $global_phase;
122             }
123              
124             {
125             package # hide
126             Devel::GlobalPhase::_Tie;
127              
128             sub TIESCALAR { bless \(my $s), $_[0]; }
129             sub STORE { die sprintf "Modification of a read-only value attempted at %s line %s.\n", (caller(0))[1,2]; }
130             *FETCH = \&Devel::GlobalPhase::global_phase;
131             sub DESTROY {
132             my $tied = tied ${^GLOBAL_PHASE};
133             if ($tied && $tied == $_[0]) {
134             untie ${^GLOBAL_PHASE};
135             my $phase = Devel::GlobalPhase::global_phase;
136             Internals::SvREADONLY($phase, 1) if defined &Internals::SvREADONLY;
137             *{^GLOBAL_PHASE} = \$phase;
138             }
139             }
140             }
141              
142             sub tie_global_phase {
143             unless ('Devel::GlobalPhase::_Tie' eq ref tied ${^GLOBAL_PHASE}) {
144             tie ${^GLOBAL_PHASE}, 'Devel::GlobalPhase::_Tie';
145             }
146             1;
147             }
148              
149             1;
150             END_CODE
151             }
152             }
153              
154             1;
155              
156             __END__