File Coverage

blib/lib/Arguments.pm
Criterion Covered Total %
statement 42 115 36.5
branch 0 30 0.0
condition 0 3 0.0
subroutine 15 19 78.9
pod 0 3 0.0
total 57 170 33.5


line stmt bran cond sub pod time code
1             #
2             # Arguments.pm -- Perl subroutine type-checking
3             #
4             # $Id: Arguments.pm,v 1.2 2001/10/07 21:38:35 binkley Exp $
5             #
6              
7             package Arguments;
8              
9 1     1   13063 use v5.6;
  1         4  
  1         56  
10 1     1   7 use strict;
  1         1  
  1         34  
11 1     1   5 use warnings;
  1         6  
  1         47  
12              
13 1         73 use constant RCSID =>
14 1     1   5 '$Id: Arguments.pm,v 1.2 2001/10/07 21:38:35 binkley Exp $';
  1         2  
15              
16 1     1   5 use Carp ( );
  1         1  
  1         21  
17 1     1   15417 use Devel::Peek ( );
  1         730  
  1         43  
18              
19             # 'our' won't work since we haven't been processed yet when
20             # MODIFY_CODE_ATTRIBUTES is called. Weird. --bko FIXME
21 1     1   7 use vars qw(@DELAYED_CHECKS %ARGUMENT_CHECKS);
  1         2  
  1         75  
22              
23             # Evil sets in. Arrange for ourselves to be in the importer's @ISA so
24             # that MODIFY_CODE_ATTRIBUTES works without the importer needing to
25             # declare us as a base package.
26             sub import {
27 1     1   5 no strict qw(refs);
  1         2  
  1         360  
28              
29 1     1   8 my $caller = caller;
30 1         1 push @{"$caller\::ISA"}, __PACKAGE__;
  1         3819  
31             }
32              
33             # Add them rather than redefine the hash so that other modules have a
34             # chance to install their own during BEGIN before we are compiled.
35             $ARGUMENT_CHECKS{REF} ||= sub { UNIVERSAL::isa ($_[0], $_[1]) };
36             $ARGUMENT_CHECKS{RX} ||= sub { defined $_[0] and $_[0] =~ $_[1] };
37              
38             # Convience.
39             our $Arguments_Package = __PACKAGE__;
40              
41             # For error messages
42             sub _quote_strings {
43 0     0   0 my @s;
44              
45 0         0 for (@_) {
46 0 0       0 push @s, (defined $_ ? do {
47 0         0 my $s = $_;
48 0         0 $s =~ s/\\/\\\\/g;
49 0         0 $s =~ s/'/\\'/g;
50 0         0 "'$s'";
51             } : 'undef');
52             }
53              
54 0         0 @s;
55             }
56              
57             # Cribbed from dumpvar.pl.
58             sub find_sub_name ($) {
59 0     0 0 0 my $code = shift;
60 0         0 $code = \&$code; # guarantee a hard reference
61              
62 0 0       0 my $gv = Devel::Peek::CvGV ($code) or return;
63              
64 0         0 *$gv{PACKAGE} . '::' . *$gv{NAME};
65             }
66              
67             # We don't want to be bothered by "%s package attribute may clash with
68             # future reserved word: %s" for MODIFY_CODE_ATTRIBUTES. HOW DO YOU
69             # MAKE THIS WORK?? --bko XXX
70             {
71 1     1   6 no warnings qw(reserved);
  1         2  
  1         485  
72              
73             sub MODIFY_CODE_ATTRIBUTES {
74 0     0   0 my ($package, $coderef, @attributes) = @_;
75              
76 0         0 my @arguments = map {
77 0         0 my $s = $_;
78 0         0 $s =~ s/^$Arguments_Package\s*\(\s*//;
79 0         0 $s =~ s/\s*\)$//;
80 0         0 split /\s*,\s*/, $s;
81             } grep /^$Arguments_Package\s*\(/, @attributes;
82              
83 0         0 if (0) {
84             # Collect the true source of any problems.
85             my @caller = qw(package filename line subroutine hasargs wantarray
86             evaltext is_require hints bitmask);
87             my %caller;
88             @caller{@caller} = do { package DB; caller (1) };
89              
90             push @DELAYED_CHECKS,
91             [$package, $coderef, [@arguments], {%caller}];
92              
93             } else {
94 0         0 my $longmess;
95              
96             {
97 0         0 local $Carp::CarpLevel = 1;
  0         0  
98 0         0 $longmess = Carp::longmess ('');
99             }
100              
101 0         0 $longmess =~ s/\n.*//s;
102              
103             # The funky last argument is so that croak looks right
104 0         0 push @DELAYED_CHECKS,
105             [$package, $coderef, [@arguments], $longmess];
106             }
107              
108 0         0 grep !/^$Arguments_Package\s*\(/, @attributes;
109             }
110             }
111              
112             sub synthesize_call_wrapper ($$$$@) {
113 0     0 0 0 my ($package, $sub_name, $prototype, $longmess, @arguments) = @_;
114 0         0 my $required = grep !/\?$/, @arguments;
115 0         0 my $optional = @arguments;
116              
117 0         0 my $coderef;
118 1     1   5 { no strict qw(refs); $coderef = *{$sub_name}{CODE} }
  1         2  
  1         426  
  0         0  
  0         0  
  0         0  
119              
120 0         0 my $s = "sub ($prototype) {
121             Carp::croak \"Not enough arguments for $sub_name\"
122             if \@_ < $required;
123             Carp::croak \"Too many arguments for $sub_name\"
124             if \@_ > $optional;
125             ";
126              
127 0         0 my $i = 0;
128              
129 0         0 for my $a (@arguments) {
130 0         0 my $j = $i + 1;
131              
132 0         0 $s .= " Carp::croak \"Type of arg $j to $sub_name must be $a (not \"
133             . defined \$_[$i] ? \$_[$i] : 'undef' . ')'
134             ";
135              
136             # How to handle these? --bko FIXME
137 0         0 my $opt = $a =~ s/\?$//;
138              
139 0 0       0 if (exists $ARGUMENT_CHECKS{$a}) {
    0          
140 0         0 $s .= " unless \$ARGUMENT_CHECKS{'$a'}->(\$_[$i]);
141             ";
142              
143             } elsif ($a =~ /^\//) {
144 0         0 eval "use strict; use warnings; qr$a";
145              
146 0 0       0 if ($@) {
147             # Hide the eval
148 0         0 my ($s) = $@ =~ /(.*) at .*$/;
149 0         0 Carp::croak "$s$longmess.\n"; # test RX first
150             }
151              
152 0         0 $s .= " unless \$ARGUMENT_CHECKS{RX}->(\$_[$i], qr$a);
153             ";
154              
155             } else {
156 0         0 eval "use strict; use warnings; \${'$a'};";
157              
158 0 0       0 if ($@) {
159             # Hide the eval
160 0         0 my ($s) = $@ =~ /(.*) at .*$/;
161 0         0 Carp::croak "$s$longmess.\n"; # test RX first
162             }
163              
164              
165 0         0 $s .= " unless \$ARGUMENT_CHECKS{REF}->(\$_[$i], '$a');
166             ";
167             }
168              
169 0         0 $i = $j;
170             }
171              
172 0         0 $s .= "
173             goto &\$coderef;
174             };
175             ";
176              
177             # The 'misc' warning is weird -- eval keep seeing "unrecognized
178             # escape \d" while dealing with /^\d+$/ (an unsigned integer), which
179             # doesn't seem right. --bko FIXME
180 1     1   5 { no strict qw(refs); no warnings qw(misc redefine);
  1     1   2  
  1         40  
  1         5  
  1         2  
  1         449  
  0         0  
181 0         0 *{$sub_name} = eval $s }
  0         0  
182              
183 0 0       0 croak $@ if $@;
184             }
185              
186             sub process_delayed_checks ( ) {
187 1     1 0 1272 for (@DELAYED_CHECKS) {
188 0           my ($package, $coderef, $arguments, $longmess) = @$_;
189 0           my @arguments = @$arguments;
190              
191 0           my $prototype = prototype $coderef;
192              
193             # Normal, variadic sub.
194 0 0 0       return if not defined $prototype and not @arguments;
195              
196 0           my $sub_name = find_sub_name $coderef;
197 0           my (@prototypes, $ref, $opt);
198              
199 0           for my $token (split //, $prototype) {
200 0 0         if ($ref) {
    0          
    0          
201 0           undef $ref;
202 0 0         push @prototypes, $opt ? "\\$token?" : "\\$token";
203              
204             } elsif ($token eq ';') {
205 0           $opt = 1;
206              
207             } elsif ($token eq "\\") {
208 0           $ref = 1;
209              
210             } else {
211 0 0         push @prototypes, $opt ? "$token?" : $token;
212             }
213             }
214              
215             # Check that they match. Use the "\n" trick from Carp::Heavy.
216 0 0         Carp::croak "Not enough prototypes for $sub_name$longmess.\n"
217             if @arguments < @prototypes;
218 0 0         Carp::croak "Too many prototypes for $sub_name$longmess.\n"
219             if @arguments > @prototypes;
220              
221 0           synthesize_call_wrapper
222             ($package, $sub_name, $prototype, $longmess, @arguments);
223             }
224             }
225              
226             # Work around that subs don't have prototypes defined yet at the time
227             # that attributes are processed. I'd consider this a bug. --bko FIXME
228             {
229             # We need to be in the main package do delay our processing until
230             # all the other packages have had a chance to declare and/or define
231             # their prototypes. Otherwise, we get called too soon, and
232             # encounter the 'no prototypes' bug. --bko FIXME
233             package main;
234              
235             CHECK {
236             # This will try to move the problem so that it shows up in the sub
237             # declaration rather that in this processing.
238 1     1   6 local $Carp::CarpLevel = 3;
239 1         8 Arguments::process_delayed_checks ( );
240             }
241             }
242              
243             1;
244              
245             __END__