Rashed Abdel-Tawab | 4db47f4 | 2019-09-06 10:38:22 -0700 | [diff] [blame] | 1 | package attributes; |
| 2 | |
| 3 | our $VERSION = 0.29; |
| 4 | |
| 5 | @EXPORT_OK = qw(get reftype); |
| 6 | @EXPORT = (); |
| 7 | %EXPORT_TAGS = (ALL => [@EXPORT, @EXPORT_OK]); |
| 8 | |
| 9 | use strict; |
| 10 | |
| 11 | sub croak { |
| 12 | require Carp; |
| 13 | goto &Carp::croak; |
| 14 | } |
| 15 | |
| 16 | sub carp { |
| 17 | require Carp; |
| 18 | goto &Carp::carp; |
| 19 | } |
| 20 | |
| 21 | my %deprecated; |
| 22 | $deprecated{CODE} = qr/\A-?(locked)\z/; |
| 23 | $deprecated{ARRAY} = $deprecated{HASH} = $deprecated{SCALAR} |
| 24 | = qr/\A-?(unique)\z/; |
| 25 | |
| 26 | my %msg = ( |
| 27 | lvalue => 'lvalue attribute applied to already-defined subroutine', |
| 28 | -lvalue => 'lvalue attribute removed from already-defined subroutine', |
| 29 | const => 'Useless use of attribute "const"', |
| 30 | ); |
| 31 | |
| 32 | sub _modify_attrs_and_deprecate { |
| 33 | my $svtype = shift; |
| 34 | # Now that we've removed handling of locked from the XS code, we need to |
| 35 | # remove it here, else it ends up in @badattrs. (If we do the deprecation in |
| 36 | # XS, we can't control the warning based on *our* caller's lexical settings, |
| 37 | # and the warned line is in this package) |
| 38 | grep { |
| 39 | $deprecated{$svtype} && /$deprecated{$svtype}/ ? do { |
| 40 | require warnings; |
| 41 | warnings::warnif('deprecated', "Attribute \"$1\" is deprecated, " . |
| 42 | "and will disappear in Perl 5.28"); |
| 43 | 0; |
| 44 | } : $svtype eq 'CODE' && exists $msg{$_} ? do { |
| 45 | require warnings; |
| 46 | warnings::warnif( |
| 47 | 'misc', |
| 48 | $msg{$_} |
| 49 | ); |
| 50 | 0; |
| 51 | } : 1 |
| 52 | } _modify_attrs(@_); |
| 53 | } |
| 54 | |
| 55 | sub import { |
| 56 | @_ > 2 && ref $_[2] or do { |
| 57 | require Exporter; |
| 58 | goto &Exporter::import; |
| 59 | }; |
| 60 | my (undef,$home_stash,$svref,@attrs) = @_; |
| 61 | |
| 62 | my $svtype = uc reftype($svref); |
| 63 | my $pkgmeth; |
| 64 | $pkgmeth = UNIVERSAL::can($home_stash, "MODIFY_${svtype}_ATTRIBUTES") |
| 65 | if defined $home_stash && $home_stash ne ''; |
| 66 | my @badattrs; |
| 67 | if ($pkgmeth) { |
| 68 | my @pkgattrs = _modify_attrs_and_deprecate($svtype, $svref, @attrs); |
| 69 | @badattrs = $pkgmeth->($home_stash, $svref, @pkgattrs); |
| 70 | if (!@badattrs && @pkgattrs) { |
| 71 | require warnings; |
| 72 | return unless warnings::enabled('reserved'); |
| 73 | @pkgattrs = grep { m/\A[[:lower:]]+(?:\z|\()/ } @pkgattrs; |
| 74 | if (@pkgattrs) { |
| 75 | for my $attr (@pkgattrs) { |
| 76 | $attr =~ s/\(.+\z//s; |
| 77 | } |
| 78 | my $s = ((@pkgattrs == 1) ? '' : 's'); |
| 79 | carp "$svtype package attribute$s " . |
| 80 | "may clash with future reserved word$s: " . |
| 81 | join(' : ' , @pkgattrs); |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | else { |
| 86 | @badattrs = _modify_attrs_and_deprecate($svtype, $svref, @attrs); |
| 87 | } |
| 88 | if (@badattrs) { |
| 89 | croak "Invalid $svtype attribute" . |
| 90 | (( @badattrs == 1 ) ? '' : 's') . |
| 91 | ": " . |
| 92 | join(' : ', @badattrs); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | sub get ($) { |
| 97 | @_ == 1 && ref $_[0] or |
| 98 | croak 'Usage: '.__PACKAGE__.'::get $ref'; |
| 99 | my $svref = shift; |
| 100 | my $svtype = uc reftype($svref); |
| 101 | my $stash = _guess_stash($svref); |
| 102 | $stash = caller unless defined $stash; |
| 103 | my $pkgmeth; |
| 104 | $pkgmeth = UNIVERSAL::can($stash, "FETCH_${svtype}_ATTRIBUTES") |
| 105 | if defined $stash && $stash ne ''; |
| 106 | return $pkgmeth ? |
| 107 | (_fetch_attrs($svref), $pkgmeth->($stash, $svref)) : |
| 108 | (_fetch_attrs($svref)) |
| 109 | ; |
| 110 | } |
| 111 | |
| 112 | sub require_version { goto &UNIVERSAL::VERSION } |
| 113 | |
| 114 | require XSLoader; |
| 115 | XSLoader::load(); |
| 116 | |
| 117 | 1; |
| 118 | __END__ |
| 119 | #The POD goes here |
| 120 | |