| Filename | /home/hejohns/perl5/lib/perl5/Data/Printer/Filter/GenericClass.pm |
| Statements | Executed 12 statements in 1.38ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 1 | 1 | 1 | 8µs | 9µs | Data::Printer::Filter::GenericClass::BEGIN@2 |
| 1 | 1 | 1 | 6µs | 6µs | Data::Printer::Filter::GenericClass::BEGIN@5 |
| 1 | 1 | 1 | 4µs | 11µs | Data::Printer::Filter::GenericClass::BEGIN@6 |
| 1 | 1 | 1 | 3µs | 16µs | Data::Printer::Filter::GenericClass::BEGIN@3 |
| 1 | 1 | 1 | 3µs | 14µs | Data::Printer::Filter::GenericClass::BEGIN@4 |
| 1 | 1 | 1 | 300ns | 300ns | Data::Printer::Filter::GenericClass::__ANON__ (xsub) |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::__ANON__[:102] |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::__ANON__[:184] |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::__ANON__[:199] |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::__ANON__[:213] |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::__ANON__[:72] |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::__ANON__[:91] |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::_get_all_subs_from |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::_get_overloads |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::_get_stringification |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::_methods_of |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Filter::GenericClass::_show_methods |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package Data::Printer::Filter::GenericClass; | ||||
| 2 | 2 | 16µs | 2 | 10µs | # spent 9µs (8+1) within Data::Printer::Filter::GenericClass::BEGIN@2 which was called:
# once (8µs+1µs) by Data::Printer::Object::BEGIN@61 at line 2 # spent 9µs making 1 call to Data::Printer::Filter::GenericClass::BEGIN@2
# spent 1µs making 1 call to strict::import |
| 3 | 2 | 16µs | 2 | 28µs | # spent 16µs (3+13) within Data::Printer::Filter::GenericClass::BEGIN@3 which was called:
# once (3µs+13µs) by Data::Printer::Object::BEGIN@61 at line 3 # spent 16µs making 1 call to Data::Printer::Filter::GenericClass::BEGIN@3
# spent 13µs making 1 call to warnings::import |
| 4 | 2 | 14µs | 2 | 26µs | # spent 14µs (3+12) within Data::Printer::Filter::GenericClass::BEGIN@4 which was called:
# once (3µs+12µs) by Data::Printer::Object::BEGIN@61 at line 4 # spent 14µs making 1 call to Data::Printer::Filter::GenericClass::BEGIN@4
# spent 12µs making 1 call to Data::Printer::Filter::import |
| 5 | 2 | 15µs | 2 | 6µs | # spent 6µs (6+300ns) within Data::Printer::Filter::GenericClass::BEGIN@5 which was called:
# once (6µs+300ns) by Data::Printer::Object::BEGIN@61 at line 5 # spent 6µs making 1 call to Data::Printer::Filter::GenericClass::BEGIN@5
# spent 300ns making 1 call to Data::Printer::Filter::GenericClass::__ANON__ |
| 6 | 2 | 1.31ms | 2 | 18µs | # spent 11µs (4+7) within Data::Printer::Filter::GenericClass::BEGIN@6 which was called:
# once (4µs+7µs) by Data::Printer::Object::BEGIN@61 at line 6 # spent 11µs making 1 call to Data::Printer::Filter::GenericClass::BEGIN@6
# spent 7µs making 1 call to Exporter::import |
| 7 | |||||
| 8 | filter '-class' => sub { | ||||
| 9 | my ($object, $ddp) = @_; | ||||
| 10 | |||||
| 11 | # if the class implements its own Data::Printer method, we use it! | ||||
| 12 | if ($ddp->class_method and my $method = $object->can( $ddp->class_method )) { | ||||
| 13 | return $method->($object, $ddp) if ref $method eq 'CODE'; | ||||
| 14 | } | ||||
| 15 | |||||
| 16 | my $class_name = ref $object; | ||||
| 17 | |||||
| 18 | # there are many parts of the class filter that require the object's | ||||
| 19 | # linear ISA, so we declare it earlier and load it only once: | ||||
| 20 | my $linear_ISA = Data::Printer::Common::_linear_ISA_for($class_name, $ddp); | ||||
| 21 | |||||
| 22 | # if the object overloads stringification, use it! | ||||
| 23 | # except for PDF::API2 which has a destructive stringify() | ||||
| 24 | if ($ddp->class->stringify && $class_name ne 'PDF::API2') { | ||||
| 25 | my $str = _get_stringification($ddp, $object, $class_name); | ||||
| 26 | return $ddp->maybe_colorize("$str ($class_name)", 'class') | ||||
| 27 | if defined $str; | ||||
| 28 | } | ||||
| 29 | |||||
| 30 | # otherwise, do our generic object representation: | ||||
| 31 | my $show_reftype = $ddp->class->show_reftype; | ||||
| 32 | my $show_internals = $ddp->class->internals; | ||||
| 33 | my $reftype; | ||||
| 34 | if ($show_reftype || $show_internals) { | ||||
| 35 | $reftype = Scalar::Util::reftype($object); | ||||
| 36 | $reftype = 'Regexp' if $reftype eq 'REGEXP'; | ||||
| 37 | } | ||||
| 38 | |||||
| 39 | $ddp->{_class_depth}++; | ||||
| 40 | my $string = $ddp->maybe_colorize( $class_name, 'class' ); | ||||
| 41 | |||||
| 42 | if ($show_reftype) { | ||||
| 43 | $string .= ' ' | ||||
| 44 | . $ddp->maybe_colorize('(', 'brackets') | ||||
| 45 | . $ddp->maybe_colorize( $reftype, 'class' ) | ||||
| 46 | . $ddp->maybe_colorize(')', 'brackets'); | ||||
| 47 | } | ||||
| 48 | |||||
| 49 | if ($ddp->class->expand eq 'all' || $ddp->class->expand >= $ddp->{_class_depth}) { | ||||
| 50 | $ddp->indent; | ||||
| 51 | $string .= ' ' . $ddp->maybe_colorize('{', 'brackets'); | ||||
| 52 | |||||
| 53 | my @superclasses = Data::Printer::Common::_get_superclasses_for($class_name); | ||||
| 54 | if (@superclasses && $ddp->class->parents) { | ||||
| 55 | $string .= $ddp->newline . 'parents: ' | ||||
| 56 | . join(', ', map $ddp->maybe_colorize($_, 'class'), @superclasses) | ||||
| 57 | ; | ||||
| 58 | } | ||||
| 59 | my (%roles, %attributes); | ||||
| 60 | if ($INC{'Role/Tiny.pm'} && exists $Role::Tiny::APPLIED_TO{$class_name}) { | ||||
| 61 | %roles = %{ $Role::Tiny::APPLIED_TO{$class_name} }; | ||||
| 62 | } | ||||
| 63 | my $is_moose = 0; | ||||
| 64 | |||||
| 65 | foreach my $parent (@$linear_ISA) { | ||||
| 66 | if ($parent eq 'Moo::Object') { | ||||
| 67 | Data::Printer::Common::_tryme(sub { | ||||
| 68 | my $moo_maker = 'Moo'->_constructor_maker_for($class_name); | ||||
| 69 | if (defined $moo_maker) { | ||||
| 70 | %attributes = %{ $moo_maker->all_attribute_specs }; | ||||
| 71 | } | ||||
| 72 | }); | ||||
| 73 | last; | ||||
| 74 | } | ||||
| 75 | elsif ($parent eq 'Moose::Object') { | ||||
| 76 | Data::Printer::Common::_tryme(sub { | ||||
| 77 | my $class_meta = $class_name->meta; | ||||
| 78 | $is_moose = 1; | ||||
| 79 | %attributes = map { | ||||
| 80 | $_->name => { | ||||
| 81 | index => $_->insertion_order, | ||||
| 82 | init_arg => $_->init_arg, | ||||
| 83 | is => (defined $_->writer ? 'rw' : 'ro'), | ||||
| 84 | reader => $_->reader, | ||||
| 85 | required => $_->is_required, | ||||
| 86 | } | ||||
| 87 | } $class_meta->get_all_attributes(); | ||||
| 88 | foreach my $role ($class_meta->calculate_all_roles()) { | ||||
| 89 | $roles{ $role->name } = 1; | ||||
| 90 | } | ||||
| 91 | }); | ||||
| 92 | last; | ||||
| 93 | } | ||||
| 94 | elsif ($parent eq 'Object::Pad::UNIVERSAL') { | ||||
| 95 | Data::Printer::Common::_tryme(sub { | ||||
| 96 | my $meta = Object::Pad::MOP::Class->for_class( $class_name ); | ||||
| 97 | %attributes = map { | ||||
| 98 | $_->name . $_->value($class_name) => { | ||||
| 99 | } | ||||
| 100 | } $meta->fields; | ||||
| 101 | %roles = map { $_->name => 1 } $meta->direct_roles; | ||||
| 102 | }); | ||||
| 103 | } | ||||
| 104 | } | ||||
| 105 | if ($ddp->class->show_methods ne 'none') { | ||||
| 106 | if (my @role_list = keys %roles) { | ||||
| 107 | @role_list = Data::Printer::Common::_nsort(@role_list) | ||||
| 108 | if @role_list && $ddp->class->sort_methods; | ||||
| 109 | $string .= $ddp->newline . 'roles (' . scalar(@role_list) . '): ' | ||||
| 110 | . join(', ' => map $ddp->maybe_colorize($_, 'class'), @role_list) | ||||
| 111 | ; | ||||
| 112 | } | ||||
| 113 | |||||
| 114 | if (my @attr_list = keys %attributes) { | ||||
| 115 | @attr_list = Data::Printer::Common::_nsort(@attr_list) | ||||
| 116 | if @attr_list && $ddp->class->sort_methods; | ||||
| 117 | $string .= $ddp->newline . 'attributes (' . scalar(@attr_list) . '): ' | ||||
| 118 | . join(', ' => map $ddp->maybe_colorize($_, 'method'), @attr_list) | ||||
| 119 | ; | ||||
| 120 | } | ||||
| 121 | } | ||||
| 122 | |||||
| 123 | my $show_linear_isa = $ddp->class->linear_isa && ( | ||||
| 124 | ($ddp->class->linear_isa eq 'auto' and @superclasses > 1) | ||||
| 125 | or ($ddp->class->linear_isa ne 'auto') | ||||
| 126 | ); | ||||
| 127 | |||||
| 128 | if ($show_linear_isa && @$linear_ISA) { | ||||
| 129 | $string .= $ddp->newline . 'linear @ISA: ' | ||||
| 130 | . join(', ' => map $ddp->maybe_colorize($_, 'class'), @$linear_ISA) | ||||
| 131 | ; | ||||
| 132 | } | ||||
| 133 | |||||
| 134 | if ($ddp->class->show_methods ne 'none') { | ||||
| 135 | $string .= _show_methods($class_name, $linear_ISA, \%attributes, $ddp); | ||||
| 136 | if ($is_moose && $ddp->class->show_wrapped) { | ||||
| 137 | my $modified = ''; | ||||
| 138 | my $modified_count = 0; | ||||
| 139 | $ddp->indent; | ||||
| 140 | for my $method ($class_name->meta->get_all_methods) { | ||||
| 141 | if (ref $method eq 'Class::MOP::Method::Wrapped') { | ||||
| 142 | foreach my $kind (qw(before around after)) { | ||||
| 143 | my $getter_method = $kind . '_modifiers'; | ||||
| 144 | if (my @modlist = $method->$getter_method) { | ||||
| 145 | $modified .= $ddp->newline . $kind . ' ' . $method->name . ': ' | ||||
| 146 | . (@modlist > 1 ? $ddp->parse(\@modlist) : $ddp->parse($modlist[0])); | ||||
| 147 | $modified_count++; | ||||
| 148 | } | ||||
| 149 | } | ||||
| 150 | } | ||||
| 151 | } | ||||
| 152 | $ddp->outdent; | ||||
| 153 | if ($modified_count) { | ||||
| 154 | $string .= $ddp->newline . 'method modifiers (' . $modified_count . '):' | ||||
| 155 | . $modified; | ||||
| 156 | } | ||||
| 157 | } | ||||
| 158 | } | ||||
| 159 | |||||
| 160 | if ($ddp->class->show_overloads) { | ||||
| 161 | my @overloads = _get_overloads($object); | ||||
| 162 | if (@overloads) { | ||||
| 163 | $string .= $ddp->newline . 'overloads: ' . join(', ' => @overloads); | ||||
| 164 | } | ||||
| 165 | } | ||||
| 166 | |||||
| 167 | if ($show_internals) { | ||||
| 168 | $string .= $ddp->newline | ||||
| 169 | . 'internals: ' | ||||
| 170 | . $ddp->parse_as($reftype, $object) | ||||
| 171 | ; | ||||
| 172 | } | ||||
| 173 | |||||
| 174 | $ddp->outdent; | ||||
| 175 | $string .= $ddp->newline . $ddp->maybe_colorize('}', 'brackets'); | ||||
| 176 | } | ||||
| 177 | $ddp->{_class_depth}--; | ||||
| 178 | |||||
| 179 | if ($ddp->show_tied and my $tie = ref tied $object) { | ||||
| 180 | $string .= " (tied to $tie)"; | ||||
| 181 | } | ||||
| 182 | |||||
| 183 | return $string; | ||||
| 184 | 1 | 5µs | 1 | 16µs | }; # spent 16µs making 1 call to Data::Printer::Filter::__ANON__[Data/Printer/Filter.pm:23] |
| 185 | |||||
| 186 | ####################################### | ||||
| 187 | ### Private auxiliary helpers below ### | ||||
| 188 | ####################################### | ||||
| 189 | |||||
| 190 | sub _get_stringification { | ||||
| 191 | my ($ddp, $object, $class_name) = @_; | ||||
| 192 | require overload; | ||||
| 193 | if (overload::Overloaded($object) | ||||
| 194 | && (overload::Method($object, q("")) | ||||
| 195 | || overload::Method($object, q(0+)) | ||||
| 196 | ) | ||||
| 197 | ) { | ||||
| 198 | my $string; | ||||
| 199 | my $error = Data::Printer::Common::_tryme(sub { $string = '' . $object }); | ||||
| 200 | if ($error) { | ||||
| 201 | Data::Printer::Common::_warn( | ||||
| 202 | $ddp, | ||||
| 203 | "string/number overload error for object $class_name: $error" | ||||
| 204 | ); | ||||
| 205 | } | ||||
| 206 | else { | ||||
| 207 | return $string; | ||||
| 208 | } | ||||
| 209 | } | ||||
| 210 | foreach my $method (qw(as_string stringify to_string)) { | ||||
| 211 | if ($object->can($method)) { | ||||
| 212 | my $string; | ||||
| 213 | my $error = Data::Printer::Common::_tryme(sub { $string = $object->$method }); | ||||
| 214 | if ($error) { | ||||
| 215 | Data::Printer::Common::_warn( | ||||
| 216 | $ddp, | ||||
| 217 | "error stringifying object $class_name with $method\(\): $error" | ||||
| 218 | ); | ||||
| 219 | } | ||||
| 220 | else { | ||||
| 221 | return $string; | ||||
| 222 | } | ||||
| 223 | } | ||||
| 224 | } | ||||
| 225 | return; | ||||
| 226 | } | ||||
| 227 | |||||
| 228 | # returns array of all overloads in class; | ||||
| 229 | sub _get_overloads { | ||||
| 230 | my ($object) = @_; | ||||
| 231 | require overload; | ||||
| 232 | return () unless overload::Overloaded($object); | ||||
| 233 | return sort grep overload::Method($object, $_), | ||||
| 234 | map split(/\s+/), values %overload::ops; | ||||
| 235 | } | ||||
| 236 | |||||
| 237 | sub _show_methods { | ||||
| 238 | my ($class_name, $linear_ISA, $attributes, $ddp) = @_; | ||||
| 239 | |||||
| 240 | my %methods = ( public => {}, private => {} ); | ||||
| 241 | my @all_methods = map _methods_of( | ||||
| 242 | $_, Data::Printer::Common::_get_namespace($_) | ||||
| 243 | ), @$linear_ISA; | ||||
| 244 | my $show_methods = $ddp->class->show_methods; | ||||
| 245 | my $show_inherited = $ddp->class->inherited; | ||||
| 246 | my %seen_method_name; | ||||
| 247 | foreach my $method (@all_methods) { | ||||
| 248 | my ($package_string, $method_string) = @$method; | ||||
| 249 | next if exists $attributes->{$method_string}; | ||||
| 250 | next if $seen_method_name{$method_string}++; | ||||
| 251 | next if $method_string eq '__ANON__'; # anonymous subs don't matter here. | ||||
| 252 | my $type = substr($method_string, 0, 1) eq '_' ? 'private' : 'public'; | ||||
| 253 | if ($package_string eq $class_name) { | ||||
| 254 | next unless $show_methods eq 'all' || $show_methods eq $type; | ||||
| 255 | $methods{$type}{$method_string} = undef; | ||||
| 256 | } | ||||
| 257 | else { | ||||
| 258 | next unless $show_inherited eq 'all' || $show_inherited eq $type; | ||||
| 259 | $methods{$type}{$method_string} = $package_string; | ||||
| 260 | } | ||||
| 261 | } | ||||
| 262 | my $string = ''; | ||||
| 263 | foreach my $type (qw(public private)) { | ||||
| 264 | next unless $show_methods eq 'all' or $show_methods eq $type | ||||
| 265 | or $show_inherited eq 'all' or $show_inherited eq $type | ||||
| 266 | ; | ||||
| 267 | if ($ddp->class->format_inheritance eq 'string') { | ||||
| 268 | my @method_list = keys %{$methods{$type}}; | ||||
| 269 | @method_list = Data::Printer::Common::_nsort(@method_list) | ||||
| 270 | if @method_list && $ddp->class->sort_methods; | ||||
| 271 | |||||
| 272 | $string .= $ddp->newline . "$type methods (" . scalar(@method_list) . ')'; | ||||
| 273 | if (@method_list) { | ||||
| 274 | $string .= ': ' | ||||
| 275 | . join(', ' => map { | ||||
| 276 | $ddp->maybe_colorize( | ||||
| 277 | $_ . (defined $methods{$type}{$_} ? " ($methods{$type}{$_})" : ''), | ||||
| 278 | 'method' | ||||
| 279 | ) | ||||
| 280 | } @method_list) | ||||
| 281 | ; | ||||
| 282 | } | ||||
| 283 | } | ||||
| 284 | else { # 'lines' | ||||
| 285 | # first we convert our hash to { pkg => [ @methods ] } | ||||
| 286 | my %lined_methods; | ||||
| 287 | my @base_methods; | ||||
| 288 | my $total_methods = 0; | ||||
| 289 | foreach my $method (keys %{$methods{$type}}) { | ||||
| 290 | my $pkg_name = $methods{$type}{$method}; | ||||
| 291 | if (defined $pkg_name) { | ||||
| 292 | push @{ $lined_methods{$pkg_name} }, $method; | ||||
| 293 | } | ||||
| 294 | else { | ||||
| 295 | push @base_methods, $method; | ||||
| 296 | } | ||||
| 297 | $total_methods++; | ||||
| 298 | } | ||||
| 299 | |||||
| 300 | # then we print them, starting with our own methods: | ||||
| 301 | @base_methods = Data::Printer::Common::_nsort(@base_methods) | ||||
| 302 | if @base_methods && $ddp->class->sort_methods; | ||||
| 303 | |||||
| 304 | $string .= $ddp->newline . "$type methods ($total_methods)" | ||||
| 305 | . ($total_methods ? ':' : '') | ||||
| 306 | ; | ||||
| 307 | if (@base_methods) { | ||||
| 308 | my $base_string = join(', ' => map { | ||||
| 309 | $ddp->maybe_colorize($_, 'method') | ||||
| 310 | } @base_methods); | ||||
| 311 | $ddp->indent; | ||||
| 312 | # newline only if we have parent methods to show: | ||||
| 313 | $string .= (keys %lined_methods ? $ddp->newline : ' ') . $base_string; | ||||
| 314 | $ddp->outdent; | ||||
| 315 | } | ||||
| 316 | foreach my $pkg (sort keys %lined_methods) { | ||||
| 317 | $ddp->indent; | ||||
| 318 | $string .= $ddp->newline . "$pkg:"; | ||||
| 319 | @{$lined_methods{$pkg}} = Data::Printer::Common::_nsort(@{$lined_methods{$pkg}}) | ||||
| 320 | if $ddp->class->sort_methods; | ||||
| 321 | $ddp->indent; | ||||
| 322 | $string .= $ddp->newline . join(', ' => map { | ||||
| 323 | $ddp->maybe_colorize($_, 'method') | ||||
| 324 | } @{$lined_methods{$pkg}} | ||||
| 325 | ); | ||||
| 326 | $ddp->outdent; | ||||
| 327 | $ddp->outdent; | ||||
| 328 | } | ||||
| 329 | } | ||||
| 330 | } | ||||
| 331 | |||||
| 332 | return $string; | ||||
| 333 | } | ||||
| 334 | |||||
| 335 | sub _methods_of { | ||||
| 336 | require B; | ||||
| 337 | my ($class_name, $namespace) = @_; | ||||
| 338 | my @methods; | ||||
| 339 | foreach my $subref (_get_all_subs_from($class_name, $namespace)) { | ||||
| 340 | next unless $subref; | ||||
| 341 | my $m = B::svref_2object($subref); | ||||
| 342 | next unless $m && $m->isa('B::CV'); | ||||
| 343 | my $gv = $m->GV; | ||||
| 344 | next unless $gv && !$gv->isa('B::Special') && $gv->NAME; | ||||
| 345 | push @methods, [ $gv->STASH->NAME, $gv->NAME ]; | ||||
| 346 | } | ||||
| 347 | return @methods; | ||||
| 348 | } | ||||
| 349 | |||||
| 350 | sub _get_all_subs_from { | ||||
| 351 | my ($class_name, $namespace) = @_; | ||||
| 352 | my @subs; | ||||
| 353 | foreach my $key (keys %$namespace) { | ||||
| 354 | # perlsub says any sub starting with '(' is reserved for overload, | ||||
| 355 | # so we skip those: | ||||
| 356 | next if substr($key, 0, 1) eq '('; | ||||
| 357 | if ( | ||||
| 358 | # any non-typeglob in the symbol table is a constant or stub | ||||
| 359 | ref(\$namespace->{$key}) ne 'GLOB' | ||||
| 360 | # regular subs are stored in the CODE slot of the typeglob | ||||
| 361 | || defined(*{$namespace->{$key}}{CODE}) | ||||
| 362 | ) { | ||||
| 363 | push @subs, $key; | ||||
| 364 | } | ||||
| 365 | } | ||||
| 366 | my @symbols; | ||||
| 367 | foreach my $sub (@subs) { | ||||
| 368 | push @symbols, Data::Printer::Common::_get_symbol($class_name, $namespace, $sub, 'CODE'); | ||||
| 369 | } | ||||
| 370 | return @symbols; | ||||
| 371 | } | ||||
| 372 | |||||
| 373 | 1 | 2µs | 1; | ||
# spent 300ns within Data::Printer::Filter::GenericClass::__ANON__ which was called:
# once (300ns+0s) by Data::Printer::Filter::GenericClass::BEGIN@5 at line 5 |