blob: 6998dba681698da5c2d3233f0e6562f66b715412 [file] [log] [blame]
Rashed Abdel-Tawab4db47f42019-09-06 10:38:22 -07001use 5.008;
2package fields;
3
4require 5.005;
5use strict;
6no strict 'refs';
7unless( eval q{require warnings::register; warnings::register->import; 1} ) {
8 *warnings::warnif = sub {
9 require Carp;
10 Carp::carp(@_);
11 }
12}
13use vars qw(%attr $VERSION);
14
15$VERSION = '2.23';
16$VERSION =~ tr/_//d;
17
18# constant.pm is slow
19sub PUBLIC () { 2**0 }
20sub PRIVATE () { 2**1 }
21sub INHERITED () { 2**2 }
22sub PROTECTED () { 2**3 }
23
24# The %attr hash holds the attributes of the currently assigned fields
25# per class. The hash is indexed by class names and the hash value is
26# an array reference. The first element in the array is the lowest field
27# number not belonging to a base class. The remaining elements' indices
28# are the field numbers. The values are integer bit masks, or undef
29# in the case of base class private fields (which occupy a slot but are
30# otherwise irrelevant to the class).
31
32sub import {
33 my $class = shift;
34 return unless @_;
35 my $package = caller(0);
36 # avoid possible typo warnings
37 %{"$package\::FIELDS"} = () unless %{"$package\::FIELDS"};
38 my $fields = \%{"$package\::FIELDS"};
39 my $fattr = ($attr{$package} ||= [1]);
40 my $next = @$fattr;
41
42 # Quiet pseudo-hash deprecation warning for uses of fields::new.
43 bless \%{"$package\::FIELDS"}, 'pseudohash';
44
45 if ($next > $fattr->[0]
46 and ($fields->{$_[0]} || 0) >= $fattr->[0])
47 {
48 # There are already fields not belonging to base classes.
49 # Looks like a possible module reload...
50 $next = $fattr->[0];
51 }
52 foreach my $f (@_) {
53 my $fno = $fields->{$f};
54
55 # Allow the module to be reloaded so long as field positions
56 # have not changed.
57 if ($fno and $fno != $next) {
58 require Carp;
59 if ($fno < $fattr->[0]) {
60 if ($] < 5.006001) {
61 warn("Hides field '$f' in base class") if $^W;
62 } else {
63 warnings::warnif("Hides field '$f' in base class") ;
64 }
65 } else {
66 Carp::croak("Field name '$f' already in use");
67 }
68 }
69 $fields->{$f} = $next;
70 $fattr->[$next] = ($f =~ /^_/) ? PRIVATE : PUBLIC;
71 $next += 1;
72 }
73 if (@$fattr > $next) {
74 # Well, we gave them the benefit of the doubt by guessing the
75 # module was reloaded, but they appear to be declaring fields
76 # in more than one place. We can't be sure (without some extra
77 # bookkeeping) that the rest of the fields will be declared or
78 # have the same positions, so punt.
79 require Carp;
80 Carp::croak ("Reloaded module must declare all fields at once");
81 }
82}
83
84sub inherit {
85 require base;
86 goto &base::inherit_fields;
87}
88
89sub _dump # sometimes useful for debugging
90{
91 for my $pkg (sort keys %attr) {
92 print "\n$pkg";
93 if (@{"$pkg\::ISA"}) {
94 print " (", join(", ", @{"$pkg\::ISA"}), ")";
95 }
96 print "\n";
97 my $fields = \%{"$pkg\::FIELDS"};
98 for my $f (sort {$fields->{$a} <=> $fields->{$b}} keys %$fields) {
99 my $no = $fields->{$f};
100 print " $no: $f";
101 my $fattr = $attr{$pkg}[$no];
102 if (defined $fattr) {
103 my @a;
104 push(@a, "public") if $fattr & PUBLIC;
105 push(@a, "private") if $fattr & PRIVATE;
106 push(@a, "inherited") if $fattr & INHERITED;
107 print "\t(", join(", ", @a), ")";
108 }
109 print "\n";
110 }
111 }
112}
113
114if ($] < 5.009) {
115 *new = sub {
116 my $class = shift;
117 $class = ref $class if ref $class;
118 return bless [\%{$class . "::FIELDS"}], $class;
119 }
120} else {
121 *new = sub {
122 my $class = shift;
123 $class = ref $class if ref $class;
124 require Hash::Util;
125 my $self = bless {}, $class;
126
127 # The lock_keys() prototype won't work since we require Hash::Util :(
128 &Hash::Util::lock_keys(\%$self, _accessible_keys($class));
129 return $self;
130 }
131}
132
133sub _accessible_keys {
134 my ($class) = @_;
135 return (
136 keys %{$class.'::FIELDS'},
137 map(_accessible_keys($_), @{$class.'::ISA'}),
138 );
139}
140
141sub phash {
142 die "Pseudo-hashes have been removed from Perl" if $] >= 5.009;
143 my $h;
144 my $v;
145 if (@_) {
146 if (ref $_[0] eq 'ARRAY') {
147 my $a = shift;
148 @$h{@$a} = 1 .. @$a;
149 if (@_) {
150 $v = shift;
151 unless (! @_ and ref $v eq 'ARRAY') {
152 require Carp;
153 Carp::croak ("Expected at most two array refs\n");
154 }
155 }
156 }
157 else {
158 if (@_ % 2) {
159 require Carp;
160 Carp::croak ("Odd number of elements initializing pseudo-hash\n");
161 }
162 my $i = 0;
163 @$h{grep ++$i % 2, @_} = 1 .. @_ / 2;
164 $i = 0;
165 $v = [grep $i++ % 2, @_];
166 }
167 }
168 else {
169 $h = {};
170 $v = [];
171 }
172 [ $h, @$v ];
173
174}
175
1761;
177
178__END__
179