File Coverage

blib/lib/Devel/MAT/Tool/Object/Pad/_SVs.pm
Criterion Covered Total %
statement 101 103 98.0
branch 13 22 59.0
condition 15 20 75.0
subroutine 24 24 100.0
pod 0 1 0.0
total 153 170 90.0


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2022 -- leonerd@leonerd.org.uk
5              
6 2     2   1747 use v5.14;
  2         8  
7 2     2   10 use warnings;
  2         4  
  2         120  
8              
9             package Devel::MAT::Tool::Object::Pad::_SVs;
10              
11 2     2   9 use Devel::MAT::SV;
  2         4  
  2         78  
12              
13 2     2   9 use List::Util qw( first );
  2         5  
  2         1498  
14              
15             my $field_index_for = sub {
16             my $self = shift;
17             my ( $name ) = @_;
18              
19             my $fields = $self->structtype->fields;
20              
21             return first { $fields->[$_]->name eq $name } 0 .. $#$fields;
22             };
23              
24             my $make_accessor = sub {
25             my ( $name ) = @_;
26              
27             return sub {
28 36     36   157 my $self = shift;
29 36         78 state $idx = $self->$field_index_for( $name );
30 36         249 return $self->field( $idx );
31             };
32             };
33              
34             my $make_field_accessor = sub {
35             my ( $name ) = @_;
36              
37             return sub {
38 15     15   28 my $self = shift;
39 15         21 state $idx = $self->$field_index_for( $name );
40 15         141 return $self->field( $idx );
41             };
42             };
43              
44             my $make_sv_accessor = sub {
45             my ( $name ) = @_;
46              
47             return sub {
48 11     11   2272 my $self = shift;
49 11         26 state $idx = $self->$field_index_for( $name );
50 11         116 return $self->df->sv_at( $self->field( $idx ) );
51             };
52             };
53              
54             my $make_sv_pv_accessor = sub {
55             my ( $name ) = @_;
56              
57             return sub {
58 44     44   22167 my $self = shift;
59 44         91 state $idx = $self->$field_index_for( $name );
60 44         411 return $self->df->sv_at( $self->field( $idx ) )->pv;
61             };
62             };
63              
64             my $make_sv_elems_accessor = sub {
65             my ( $name ) = @_;
66              
67             return sub {
68 16     16   29 my $self = shift;
69 16         33 state $idx = $self->$field_index_for( $name );
70 16         118 return $self->df->sv_at( $self->field( $idx ) )->elems;
71             };
72             };
73              
74             sub around
75             {
76 2     2 0 7 my ( $fqname, $code ) = @_;
77              
78 2         19 my ( $package, $basename ) = $fqname =~ m/^(.*)::(.*?)$/;
79 2 50       29 my $orig = $package->can( $basename ) or
80             die "$package cannot ->$basename";
81              
82 2     2   14 no strict 'refs';
  2         3  
  2         92  
83 2     2   9 no warnings 'redefine';
  2         3  
  2         1142  
84 2         145 *{"${package}::${basename}"} = sub {
85 3     3   25311 my $self = shift;
86 3         15 $self->$code( $orig, @_ );
87 2         7 };
88             }
89              
90             around "Devel::MAT::SV::_outrefs_matching" => sub {
91             my $self = shift; my $orig = shift;
92             my ( $match, $no_desc ) = @_;
93              
94             my @outrefs = $self->$orig( @_ );
95              
96             my $fields_at = $self->{objectpad_fields_at};
97             if( $fields_at and $fields_at != $self->addr ) {
98             my $fieldsav = $self->df->sv_at( $fields_at );
99             push @outrefs, $no_desc ? ( inferred => $fieldsav ) :
100             Devel::MAT::SV::Reference( "the Object::Pad fields AV", inferred => $fieldsav );
101             }
102              
103             return @outrefs;
104             };
105              
106             package # hide
107             Devel::MAT::Tool::Object::Pad::_RoleOrClassSV;
108              
109             *objectpad_name = $make_sv_pv_accessor->( "the name SV" );
110              
111             *objectpad_type = $make_accessor->( "type" );
112              
113             *objectpad_repr = $make_accessor->( "repr" );
114              
115             *_objectpad_direct_fields = $make_sv_elems_accessor->( "the direct fields AV" );
116             *_objectpad_fields = $make_sv_elems_accessor->( "the fields AV" );
117              
118             sub objectpad_direct_fields
119             {
120 9     9   17 my $self = shift;
121              
122 9 50       24 if( defined $self->$field_index_for( "the fields AV" ) ) {
123             # Object::Pad version 0.807 or later
124 9         109 my @fields = $self->_objectpad_fields;
125 9         592 return grep { $_->objectpad_is_direct } @fields;
  15         43  
126 0         0 die "TODO: Filter for just direct fields";
127             }
128             else {
129             # Object::Pad version before 0.807
130 0         0 return $self->_objectpad_direct_fields;
131             }
132             }
133              
134             package # hide
135             Devel::MAT::Tool::Object::Pad::_ClassSV;
136 2     2   14 use base qw( Devel::MAT::SV::C_STRUCT Devel::MAT::Tool::Object::Pad::_RoleOrClassSV );
  2         9  
  2         2217  
137              
138             *objectpad_superclass = $make_sv_accessor->( "the supermeta" );
139              
140             *objectpad_direct_roles = $make_sv_elems_accessor->( "the direct roles AV" );
141              
142             sub objectpad_fieldnames_by_idx
143             {
144 2     2   6 my $self = shift;
145 2   66     12 return $self->{objectpad_fieldnames_by_idx} //= do {
146 1         3 my @fieldnames;
147 1         5 $self->_objectpad_fieldnames_for_class( \@fieldnames, $self );
148 1         7 \@fieldnames;
149             };
150             }
151              
152             sub _objectpad_fieldnames_for_class
153             {
154 3     3   23 my $self = shift;
155 3         10 my ( $fieldnames, $classmeta, $nameprefix, $offset ) = @_;
156 3   100     14 $offset //= 0;
157              
158 3         9 my $is_class = $classmeta->objectpad_type == 0;
159              
160 3 100 100     15 if( $is_class and my $superclass = $classmeta->objectpad_superclass ) {
161 1         20 $self->_objectpad_fieldnames_for_class( $fieldnames, $superclass, $superclass->objectpad_name );
162             }
163              
164 3         43 foreach my $fieldmeta ( $classmeta->objectpad_direct_fields ) {
165 5         18 my $name = $fieldmeta->objectpad_name;
166 5         87 my $fieldix = $fieldmeta->objectpad_fieldix + $offset;
167              
168             my $fieldname = Devel::MAT::Cmd->format_note(
169 5         12 join( "/", grep { defined } $nameprefix, $name ), 1
  10         41  
170             );
171              
172 5         46 $fieldnames->[$fieldix] = "the $fieldname field";
173             }
174              
175 3 100       14 if( $is_class ) {
176 2         7 foreach my $embedding ( $classmeta->objectpad_direct_roles ) {
177 1         40 my $rolemeta = $embedding->objectpad_role;
178              
179 1         18 $self->_objectpad_fieldnames_for_class( $fieldnames, $rolemeta, $rolemeta->objectpad_name, $embedding->objectpad_offset );
180             }
181             }
182             }
183              
184             package # hide
185             Devel::MAT::Tool::Object::Pad::_RoleSV;
186 2     2   15 use base qw( Devel::MAT::SV::C_STRUCT Devel::MAT::Tool::Object::Pad::_RoleOrClassSV );
  2         5  
  2         1097  
187              
188             package # hide
189             Devel::MAT::Tool::Object::Pad::_FieldSV;
190 2     2   15 use base qw( Devel::MAT::SV::C_STRUCT );
  2         3  
  2         682  
191              
192             *objectpad_name = $make_sv_pv_accessor->( "the name SV" );
193              
194             *objectpad_is_direct = $make_field_accessor->( "is direct" );
195              
196             *objectpad_class = $make_sv_accessor->( "the class" );
197              
198             *objectpad_fieldix = $make_accessor->( "fieldix" );
199              
200             package # hide
201             Devel::MAT::Tool::Object::Pad::_MethodSV;
202 2     2   13 use base qw( Devel::MAT::SV::C_STRUCT );
  2         4  
  2         569  
203              
204             *objectpad_name = $make_sv_pv_accessor->( "the name SV" );
205              
206             *objectpad_class = $make_sv_accessor->( "the class" );
207              
208             package # hide
209             Devel::MAT::Tool::Object::Pad::_RoleEmbeddingSV;
210 2     2   14 use base qw( Devel::MAT::SV::C_STRUCT );
  2         3  
  2         573  
211              
212             *objectpad_role = $make_sv_accessor->( "the role" );
213              
214             *objectpad_class = $make_sv_accessor->( "the class" );
215              
216             *objectpad_offset = $make_accessor->( "offset" );
217              
218             package # hide
219             Devel::MAT::Tool::Object::Pad::_FieldAV;
220 2     2   12 use base qw( Devel::MAT::SV::ARRAY );
  2         4  
  2         585  
221              
222             # TODO: Devel::MAT ought to export these somehow
223             BEGIN {
224 2     2   11 *STRENGTH_STRONG = \&Devel::MAT::SV::STRENGTH_STRONG;
225 2         997 *STRENGTH_INDIRECT = \&Devel::MAT::SV::STRENGTH_INDIRECT;
226             }
227              
228             sub _outrefs
229             {
230 2     2   24 my $self = shift;
231 2         6 my ( $match, $no_desc ) = @_;
232              
233 2 50       44 my $instance = $self->df->sv_at( $self->{objectpad_instance_at} ) or
234             return $self->Devel::MAT::SV::ARRAY::_outrefs( @_ );
235              
236 2 50       43 my $package = $instance->blessed or
237             die "SV is not a blessed object instance\n";
238              
239 2 50       44 my $class = $package->objectpad_class or
240             die $package->stashname . " is not an Object::Pad class\n";
241              
242             # Try to give outrefs per index a better name by using field names
243 2         37 my $fieldnames_by_idx = $class->objectpad_fieldnames_by_idx;
244              
245 2         14 my @elems = $self->elems;
246 2         186 my @outrefs;
247              
248 2         9 foreach my $idx ( 0 .. $#elems ) {
249 10         145 my $value = $elems[$idx];
250              
251 10   33     28 my $name = $fieldnames_by_idx->[$idx] //
252             ( "element " . Devel::MAT::Cmd->format_value( $idx, index => 1 ) );
253              
254 10 50       28 if( $match & STRENGTH_STRONG ) {
255 10 50       42 push @outrefs, $no_desc ? ( strong => $value ) :
256             Devel::MAT::SV::Reference( $name, strong => $value );
257             }
258 10 50 100     589 if( $match & STRENGTH_INDIRECT and $value->type eq "REF" and !$value->{magic} and my $rv = $value->rv ) {
      66        
      66        
259 2 50       62 push @outrefs, $no_desc ? ( indirect => $rv ) :
260             Devel::MAT::SV::Reference( $name . " via RV", indirect => $rv );
261             }
262             }
263              
264 2         21 return @outrefs;
265             }
266              
267             0x55AA;