| Filename | /home/hejohns/perl5/lib/perl5/Data/Printer/Common.pm |
| Statements | Executed 48 statements in 1.77ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 11 | 1 | 1 | 65µs | 65µs | Data::Printer::Common::_filter_category_for |
| 1 | 1 | 1 | 7µs | 8µs | Data::Printer::Common::BEGIN@3 |
| 1 | 1 | 1 | 7µs | 16µs | Data::Printer::Common::BEGIN@5 |
| 1 | 1 | 1 | 7µs | 12µs | Data::Printer::Common::BEGIN@401 |
| 1 | 1 | 1 | 6µs | 20µs | Data::Printer::Common::BEGIN@206 |
| 1 | 1 | 1 | 5µs | 8µs | Data::Printer::Common::BEGIN@428 |
| 1 | 1 | 1 | 3µs | 12µs | Data::Printer::Common::BEGIN@4 |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::__ANON__[:386] |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_colorstrip |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_die |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_escape_chars |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_fetch_anyof |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_fetch_arrayref_of_scalars |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_fetch_indexes_for |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_fetch_scalar_or_default |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_get_namespace |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_get_proper_caller |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_get_superclasses_for |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_get_symbol |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_initialize_mro |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_initialize_nsort |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_linear_ISA_for |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_nsort |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_nsort_pp |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_print_escapes |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_process_string |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_reduce_string |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_tryme |
| 0 | 0 | 0 | 0s | 0s | Data::Printer::Common::_warn |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package Data::Printer::Common; | ||||
| 2 | # Private library of shared Data::Printer code. | ||||
| 3 | 2 | 14µs | 2 | 9µs | # spent 8µs (7+1000ns) within Data::Printer::Common::BEGIN@3 which was called:
# once (7µs+1000ns) by Data::Printer::BEGIN@3.28 at line 3 # spent 8µs making 1 call to Data::Printer::Common::BEGIN@3
# spent 1µs making 1 call to strict::import |
| 4 | 2 | 11µs | 2 | 20µs | # spent 12µs (3+9) within Data::Printer::Common::BEGIN@4 which was called:
# once (3µs+9µs) by Data::Printer::BEGIN@3.28 at line 4 # spent 12µs making 1 call to Data::Printer::Common::BEGIN@4
# spent 9µs making 1 call to warnings::import |
| 5 | 2 | 761µs | 2 | 25µs | # spent 16µs (7+9) within Data::Printer::Common::BEGIN@5 which was called:
# once (7µs+9µs) by Data::Printer::BEGIN@3.28 at line 5 # spent 16µs making 1 call to Data::Printer::Common::BEGIN@5
# spent 9µs making 1 call to Exporter::import |
| 6 | |||||
| 7 | 1 | 200ns | my $mro_initialized = 0; | ||
| 8 | 1 | 100ns | my $nsort_initialized; | ||
| 9 | |||||
| 10 | |||||
| 11 | # spent 65µs within Data::Printer::Common::_filter_category_for which was called 11 times, avg 6µs/call:
# 11 times (65µs+0s) by Data::Printer::Filter::__ANON__[/home/hejohns/perl5/lib/perl5/Data/Printer/Filter.pm:23] at line 18 of Data/Printer/Filter.pm, avg 6µs/call | ||||
| 12 | 11 | 3µs | my ($name) = @_; | ||
| 13 | 11 | 45µs | my %core_types = map { $_ => 1 } | ||
| 14 | qw(SCALAR LVALUE ARRAY HASH REF VSTRING GLOB FORMAT Regexp CODE); | ||||
| 15 | 11 | 23µs | return exists $core_types{$name} ? 'type_filters' : 'class_filters'; | ||
| 16 | } | ||||
| 17 | |||||
| 18 | # strings are tough to process: there are control characters like "\t", | ||||
| 19 | # unicode characters to name or escape (or do nothing), max_string to | ||||
| 20 | # worry about, and every single piece of that could have its own color. | ||||
| 21 | # That, and hash keys and strings share this. So we put it all in one place. | ||||
| 22 | sub _process_string { | ||||
| 23 | my ($ddp, $string, $src_color) = @_; | ||||
| 24 | |||||
| 25 | # colorizing messes with reduce_string because we are effectively | ||||
| 26 | # adding new (invisible) characters to the string. So we need to | ||||
| 27 | # handle reduction first. But! Because we colorize string_max | ||||
| 28 | # *and* we should escape any colors already present, we need to | ||||
| 29 | # do both at the same time. | ||||
| 30 | $string = _reduce_string($ddp, $string, $src_color); | ||||
| 31 | |||||
| 32 | # now we escape all other control characters except for "\e", which was | ||||
| 33 | # already escaped in _reduce_string(), and convert any chosen charset | ||||
| 34 | # to the \x{} format. These could go in any particular order: | ||||
| 35 | $string = _escape_chars($ddp, $string, $src_color); | ||||
| 36 | $string = _print_escapes($ddp, $string, $src_color); | ||||
| 37 | |||||
| 38 | # finally, send our wrapped string: | ||||
| 39 | return $ddp->maybe_colorize($string, $src_color); | ||||
| 40 | } | ||||
| 41 | |||||
| 42 | sub _colorstrip { | ||||
| 43 | my ($string) = @_; | ||||
| 44 | $string =~ s{ \e\[ [\d;]* m }{}xmsg; | ||||
| 45 | return $string; | ||||
| 46 | } | ||||
| 47 | |||||
| 48 | sub _reduce_string { | ||||
| 49 | my ($ddp, $string, $src_color) = @_; | ||||
| 50 | my $max = $ddp->string_max; | ||||
| 51 | my $str_len = length($string); | ||||
| 52 | if ($max && $str_len && $str_len > $max) { | ||||
| 53 | my $preserve = $ddp->string_preserve; | ||||
| 54 | my $skipped_chars = $str_len - ($preserve eq 'none' ? 0 : $max); | ||||
| 55 | my $skip_message = $ddp->maybe_colorize( | ||||
| 56 | $ddp->string_overflow, | ||||
| 57 | 'caller_info', | ||||
| 58 | undef, | ||||
| 59 | $src_color | ||||
| 60 | ); | ||||
| 61 | $skip_message =~ s/__SKIPPED__/$skipped_chars/g; | ||||
| 62 | if ($preserve eq 'end') { | ||||
| 63 | substr $string, 0, $skipped_chars, ''; | ||||
| 64 | $string =~ s{\e}{$ddp->maybe_colorize('\\e', 'escaped', undef, $src_color)}ge | ||||
| 65 | if $ddp->print_escapes; | ||||
| 66 | $string = $skip_message . $string; | ||||
| 67 | } | ||||
| 68 | elsif ($preserve eq 'begin') { | ||||
| 69 | $string = substr($string, 0, $max); | ||||
| 70 | $string =~ s{\e}{$ddp->maybe_colorize('\\e', 'escaped', undef, $src_color)}ge | ||||
| 71 | if $ddp->print_escapes; | ||||
| 72 | $string = $string . $skip_message; | ||||
| 73 | } | ||||
| 74 | elsif ($preserve eq 'extremes') { | ||||
| 75 | my $leftside_chars = int($max / 2); | ||||
| 76 | my $rightside_chars = $max - $leftside_chars; | ||||
| 77 | my $leftside = substr($string, 0, $leftside_chars); | ||||
| 78 | my $rightside = substr($string, -$rightside_chars); | ||||
| 79 | if ($ddp->print_escapes) { | ||||
| 80 | $leftside =~ s{\e}{$ddp->maybe_colorize('\\e', 'escaped', undef, $src_color)}ge; | ||||
| 81 | $rightside =~ s{\e}{$ddp->maybe_colorize('\\e', 'escaped', undef, $src_color)}ge; | ||||
| 82 | } | ||||
| 83 | $string = $leftside . $skip_message . $rightside; | ||||
| 84 | } | ||||
| 85 | elsif ($preserve eq 'middle') { | ||||
| 86 | my $string_middle = int($str_len / 2); | ||||
| 87 | my $middle_substr = int($max / 2); | ||||
| 88 | my $substr_begin = $string_middle - $middle_substr; | ||||
| 89 | my $message_begin = $ddp->string_overflow; | ||||
| 90 | $message_begin =~ s/__SKIPPED__/$substr_begin/gs; | ||||
| 91 | my $chars_left = $str_len - ($substr_begin + $max); | ||||
| 92 | my $message_end = $ddp->string_overflow; | ||||
| 93 | $message_end =~ s/__SKIPPED__/$chars_left/gs; | ||||
| 94 | $string = substr($string, $substr_begin, $max); | ||||
| 95 | $string =~ s{\e}{$ddp->maybe_colorize('\\e', 'escaped', undef, $src_color)}ge | ||||
| 96 | if $ddp->print_escapes; | ||||
| 97 | $string = $ddp->maybe_colorize($message_begin, 'caller_info', undef, $src_color) | ||||
| 98 | . $string | ||||
| 99 | . $ddp->maybe_colorize($message_end, 'caller_info', undef, $src_color) | ||||
| 100 | ; | ||||
| 101 | } | ||||
| 102 | else { | ||||
| 103 | # preserving 'none' only shows the skipped message: | ||||
| 104 | $string = $skip_message; | ||||
| 105 | } | ||||
| 106 | } | ||||
| 107 | else { | ||||
| 108 | # nothing to do? ok, then escape any colors already present: | ||||
| 109 | $string =~ s{\e}{$ddp->maybe_colorize('\\e', 'escaped', undef, $src_color)}ge | ||||
| 110 | if $ddp->print_escapes; | ||||
| 111 | } | ||||
| 112 | return $string; | ||||
| 113 | } | ||||
| 114 | |||||
| 115 | |||||
| 116 | # _escape_chars() replaces characters with their "escaped" versions. | ||||
| 117 | # Because it may be called on scalars or (scalar) hash keys and they | ||||
| 118 | # have different colors, we need to be aware of that. | ||||
| 119 | sub _escape_chars { | ||||
| 120 | my ($ddp, $scalar, $src_color) = @_; | ||||
| 121 | |||||
| 122 | my $escape_kind = $ddp->escape_chars; | ||||
| 123 | my %target_for = ( | ||||
| 124 | nonascii => '[^\x{00}-\x{7f}]+', | ||||
| 125 | nonlatin1 => '[^\x{00}-\x{ff}]+', | ||||
| 126 | ); | ||||
| 127 | |||||
| 128 | if ($ddp->unicode_charnames) { | ||||
| 129 | require charnames; | ||||
| 130 | if ($escape_kind eq 'all') { | ||||
| 131 | $scalar = join('', map { sprintf '\N{%s}', charnames::viacode(ord $_) } split //, $scalar); | ||||
| 132 | $scalar = $ddp->maybe_colorize($scalar, 'escaped'); | ||||
| 133 | } | ||||
| 134 | else { | ||||
| 135 | $scalar =~ s{($target_for{$escape_kind})}{$ddp->maybe_colorize( (join '', map { sprintf '\N{%s}', charnames::viacode(ord $_) } split //, $1), 'escaped', undef, $src_color)}ge if exists $target_for{$escape_kind}; | ||||
| 136 | } | ||||
| 137 | } | ||||
| 138 | elsif ($escape_kind eq 'all') { | ||||
| 139 | $scalar = join('', map { sprintf '\x{%02x}', ord $_ } split //, $scalar); | ||||
| 140 | $scalar = $ddp->maybe_colorize($scalar, 'escaped'); | ||||
| 141 | } | ||||
| 142 | else { | ||||
| 143 | $scalar =~ s{($target_for{$escape_kind})}{$ddp->maybe_colorize((join '', map { sprintf '\x{%02x}', ord $_ } split //, $1), 'escaped', undef, $src_color)}ge if exists $target_for{$escape_kind}; | ||||
| 144 | } | ||||
| 145 | return $scalar; | ||||
| 146 | } | ||||
| 147 | |||||
| 148 | # _print_escapes() prints invisible chars if they exist on a string. | ||||
| 149 | # Because it may be called on scalars or (scalar) hash keys and they | ||||
| 150 | # have different colors, we need to be aware of that. Also, \e is | ||||
| 151 | # deliberately omitted because it was escaped from the original | ||||
| 152 | # string earlier, and the \e's we have now are our own colorized | ||||
| 153 | # output. | ||||
| 154 | sub _print_escapes { | ||||
| 155 | my ($ddp, $string, $src_color) = @_; | ||||
| 156 | |||||
| 157 | # always escape the null character | ||||
| 158 | $string =~ s/\0/$ddp->maybe_colorize('\\0', 'escaped', undef, $src_color)/ge; | ||||
| 159 | |||||
| 160 | return $string unless $ddp->print_escapes; | ||||
| 161 | |||||
| 162 | my %escaped = ( | ||||
| 163 | "\n" => '\n', # line feed | ||||
| 164 | "\r" => '\r', # carriage return | ||||
| 165 | "\t" => '\t', # horizontal tab | ||||
| 166 | "\f" => '\f', # formfeed | ||||
| 167 | "\b" => '\b', # backspace | ||||
| 168 | "\a" => '\a', # alert (bell) | ||||
| 169 | ); | ||||
| 170 | foreach my $k ( keys %escaped ) { | ||||
| 171 | $string =~ s/$k/$ddp->maybe_colorize($escaped{$k}, 'escaped', undef, $src_color)/ge; | ||||
| 172 | } | ||||
| 173 | return $string; | ||||
| 174 | } | ||||
| 175 | |||||
| 176 | sub _initialize_nsort { | ||||
| 177 | return 'Sort::Key::Natural' if $INC{'Sort/Key/Natural.pm'}; | ||||
| 178 | return 'Sort::Naturally' if $INC{'Sort/Naturally.pm'}; | ||||
| 179 | return 'Sort::Key::Natural' if !_tryme('use Sort::Key::Natural; 1;'); | ||||
| 180 | return 'Sort::Naturally' if !_tryme('use Sort::Naturally; 1;'); | ||||
| 181 | return 'core'; | ||||
| 182 | } | ||||
| 183 | |||||
| 184 | sub _nsort { | ||||
| 185 | if (!$nsort_initialized) { | ||||
| 186 | my $nsort_class = _initialize_nsort(); | ||||
| 187 | if ($nsort_class eq 'Sort::Key::Natural') { | ||||
| 188 | $nsort_initialized = \&{ $nsort_class . '::natsort' }; | ||||
| 189 | } | ||||
| 190 | elsif ($nsort_class ne 'core') { | ||||
| 191 | $nsort_initialized = \&{ $nsort_class . '::nsort' }; | ||||
| 192 | } | ||||
| 193 | else { | ||||
| 194 | $nsort_initialized = \&_nsort_pp | ||||
| 195 | } | ||||
| 196 | } | ||||
| 197 | return $nsort_initialized->(@_); | ||||
| 198 | } | ||||
| 199 | |||||
| 200 | # this is a very simple 'natural-ish' sorter, heavily inspired in | ||||
| 201 | # http://www.perlmonks.org/?node_id=657130 by thundergnat and tye | ||||
| 202 | sub _nsort_pp { | ||||
| 203 | my $i; | ||||
| 204 | my @unsorted = map lc, @_; | ||||
| 205 | foreach my $data (@unsorted) { | ||||
| 206 | 2 | 757µs | 2 | 35µs | # spent 20µs (6+15) within Data::Printer::Common::BEGIN@206 which was called:
# once (6µs+15µs) by Data::Printer::BEGIN@3.28 at line 206 # spent 20µs making 1 call to Data::Printer::Common::BEGIN@206
# spent 15µs making 1 call to warnings::unimport |
| 207 | $data =~ s/((\.0*)?)(\d+)/("\x0" x length $2) . (pack 'aNa*', 0, length $3, $3)/eg; | ||||
| 208 | $data .= ' ' . $i++; | ||||
| 209 | } | ||||
| 210 | return @_[ map { (split)[-1] } sort @unsorted ]; | ||||
| 211 | } | ||||
| 212 | |||||
| 213 | sub _fetch_arrayref_of_scalars { | ||||
| 214 | my ($props, $name) = @_; | ||||
| 215 | return [] unless exists $props->{$name} && ref $props->{$name} eq 'ARRAY'; | ||||
| 216 | my @valid; | ||||
| 217 | foreach my $option (@{$props->{$name}}) { | ||||
| 218 | if (ref $option) { | ||||
| 219 | # FIXME: because there is no object at this point, we need to check | ||||
| 220 | # the 'warnings' option ourselves. | ||||
| 221 | _warn(undef, "'$name' option requires scalar values only. Ignoring $option.") | ||||
| 222 | if !exists $props->{warnings} || !$props->{warnings}; | ||||
| 223 | next; | ||||
| 224 | } | ||||
| 225 | push @valid, $option; | ||||
| 226 | } | ||||
| 227 | return \@valid; | ||||
| 228 | } | ||||
| 229 | |||||
| 230 | sub _fetch_anyof { | ||||
| 231 | my ($props, $name, $default, $list) = @_; | ||||
| 232 | return $default unless exists $props->{$name}; | ||||
| 233 | foreach my $option (@$list) { | ||||
| 234 | return $option if $props->{$name} eq $option; | ||||
| 235 | } | ||||
| 236 | _die( | ||||
| 237 | "invalid value '$props->{$name}' for option '$name'" | ||||
| 238 | . "(must be one of: " . join(',', @$list) . ")" | ||||
| 239 | ); | ||||
| 240 | }; | ||||
| 241 | |||||
| 242 | |||||
| 243 | sub _fetch_scalar_or_default { | ||||
| 244 | my ($props, $name, $default) = @_; | ||||
| 245 | return $default unless exists $props->{$name}; | ||||
| 246 | |||||
| 247 | if (my $ref = ref $props->{$name}) { | ||||
| 248 | _die("'$name' property must be a scalar, not a reference to $ref"); | ||||
| 249 | } | ||||
| 250 | return $props->{$name}; | ||||
| 251 | } | ||||
| 252 | |||||
| 253 | sub _die { | ||||
| 254 | my ($message) = @_; | ||||
| 255 | my ($file, $line) = _get_proper_caller(); | ||||
| 256 | die '[Data::Printer] ' . $message . " at $file line $line.\n"; | ||||
| 257 | } | ||||
| 258 | |||||
| 259 | sub _warn { | ||||
| 260 | my ($ddp, $message) = @_; | ||||
| 261 | return if $ddp && !$ddp->warnings; | ||||
| 262 | my ($file, $line) = _get_proper_caller(); | ||||
| 263 | warn '[Data::Printer] ' . $message . " at $file line $line.\n"; | ||||
| 264 | } | ||||
| 265 | |||||
| 266 | sub _get_proper_caller { | ||||
| 267 | my $frame = 1; | ||||
| 268 | while (my @caller = caller($frame++)) { | ||||
| 269 | if ($caller[0] !~ /\AD(?:DP|ata::Printer)/) { | ||||
| 270 | return ($caller[1], $caller[2]); | ||||
| 271 | } | ||||
| 272 | } | ||||
| 273 | return ('n/d', 'n/d'); | ||||
| 274 | } | ||||
| 275 | |||||
| 276 | |||||
| 277 | # simple eval++ adapted from Try::Tiny. | ||||
| 278 | # returns a (true) error message if failed. | ||||
| 279 | sub _tryme { | ||||
| 280 | my ($subref_or_string) = @_; | ||||
| 281 | |||||
| 282 | my $previous_error = $@; | ||||
| 283 | my ($failed, $error); | ||||
| 284 | |||||
| 285 | if (ref $subref_or_string eq 'CODE') { | ||||
| 286 | $failed = not eval { | ||||
| 287 | local $SIG{'__DIE__'}; # make sure we don't trigger any exception hooks. | ||||
| 288 | $@ = $previous_error; | ||||
| 289 | $subref_or_string->(); | ||||
| 290 | return 1; | ||||
| 291 | }; | ||||
| 292 | $error = $@; | ||||
| 293 | } | ||||
| 294 | else { | ||||
| 295 | my $code = q(local $SIG{'__DIE__'};) . $subref_or_string; | ||||
| 296 | $failed = not eval $code; | ||||
| 297 | $error = $@; | ||||
| 298 | } | ||||
| 299 | $@ = $previous_error; | ||||
| 300 | # at this point $failed contains a true value if the eval died, | ||||
| 301 | # even if some destructor overwrote $@ as the eval was unwinding. | ||||
| 302 | return unless $failed; | ||||
| 303 | return ($error || '(unknown error)'); | ||||
| 304 | } | ||||
| 305 | |||||
| 306 | |||||
| 307 | # When printing array elements or hash keys, we may traverse all of it | ||||
| 308 | # or just a few chunks. This function returns those chunks' indexes, and | ||||
| 309 | # a scalar ref to a message whenever a chunk was skipped. | ||||
| 310 | sub _fetch_indexes_for { | ||||
| 311 | my ($array_ref, $prefix, $ddp) = @_; | ||||
| 312 | |||||
| 313 | my $max_function = $prefix . '_max'; | ||||
| 314 | my $preserve_function = $prefix . '_preserve'; | ||||
| 315 | my $overflow_function = $prefix . '_overflow'; | ||||
| 316 | my $max = $ddp->$max_function; | ||||
| 317 | my $preserve = $ddp->$preserve_function; | ||||
| 318 | |||||
| 319 | return (0 .. $#{$array_ref}) if !$max || @$array_ref <= $max; | ||||
| 320 | |||||
| 321 | my $skip_message = $ddp->maybe_colorize($ddp->$overflow_function, 'overflow'); | ||||
| 322 | if ($preserve eq 'begin' || $preserve eq 'end') { | ||||
| 323 | my $n_elements = @$array_ref - $max; | ||||
| 324 | $skip_message =~ s/__SKIPPED__/$n_elements/g; | ||||
| 325 | return $preserve eq 'begin' | ||||
| 326 | ? ((0 .. ($max - 1)), \$skip_message) | ||||
| 327 | : (\$skip_message, ($n_elements .. $#{$array_ref})) | ||||
| 328 | ; | ||||
| 329 | } | ||||
| 330 | elsif ($preserve eq 'extremes') { | ||||
| 331 | my $half_max = int($max / 2); | ||||
| 332 | my $last_index_of_chunk_one = $half_max - 1; | ||||
| 333 | my $n_elements = @$array_ref - $max; | ||||
| 334 | |||||
| 335 | my $first_index_of_chunk_two = @$array_ref - ($max - $half_max); | ||||
| 336 | $skip_message =~ s/__SKIPPED__/$n_elements/g; | ||||
| 337 | return ( | ||||
| 338 | (0 .. $last_index_of_chunk_one), | ||||
| 339 | \$skip_message, | ||||
| 340 | ($first_index_of_chunk_two .. $#{$array_ref}) | ||||
| 341 | ); | ||||
| 342 | } | ||||
| 343 | elsif ($preserve eq 'middle') { | ||||
| 344 | my $array_middle = int($#{$array_ref} / 2); | ||||
| 345 | my $first_index_to_show = $array_middle - int($max / 2); | ||||
| 346 | my $last_index_to_show = $first_index_to_show + $max - 1; | ||||
| 347 | my ($message_begin, $message_end) = ($skip_message, $skip_message); | ||||
| 348 | $message_begin =~ s/__SKIPPED__/$first_index_to_show/gse; | ||||
| 349 | my $items_left = $#{$array_ref} - $last_index_to_show; | ||||
| 350 | $message_end =~ s/__SKIPPED__/$items_left/gs; | ||||
| 351 | return ( | ||||
| 352 | \$message_begin, | ||||
| 353 | $first_index_to_show .. $last_index_to_show, | ||||
| 354 | \$message_end | ||||
| 355 | ); | ||||
| 356 | } | ||||
| 357 | else { # $preserve eq 'none' | ||||
| 358 | my $n_elements = scalar(@$array_ref); | ||||
| 359 | $skip_message =~ s/__SKIPPED__/$n_elements/g; | ||||
| 360 | return (\$skip_message); | ||||
| 361 | } | ||||
| 362 | } | ||||
| 363 | |||||
| 364 | # helpers below strongly inspired by the excellent Package::Stash: | ||||
| 365 | sub _linear_ISA_for { | ||||
| 366 | my ($class, $ddp) = @_; | ||||
| 367 | _initialize_mro($ddp) unless $mro_initialized; | ||||
| 368 | my $isa; | ||||
| 369 | if ($mro_initialized > 0) { | ||||
| 370 | $isa = mro::get_linear_isa($class); | ||||
| 371 | } | ||||
| 372 | else { | ||||
| 373 | # minimal fallback in case Class::MRO isn't available | ||||
| 374 | # (should only matter for perl < 5.009_005): | ||||
| 375 | $isa = [ $class, _get_superclasses_for($class) ]; | ||||
| 376 | } | ||||
| 377 | return [@$isa, ($ddp->class->universal ? 'UNIVERSAL' : ())]; | ||||
| 378 | } | ||||
| 379 | |||||
| 380 | sub _initialize_mro { | ||||
| 381 | my ($ddp) = @_; | ||||
| 382 | my $error = _tryme(sub { | ||||
| 383 | if ($] < 5.009_005) { require MRO::Compat } | ||||
| 384 | else { require mro } | ||||
| 385 | 1; | ||||
| 386 | }); | ||||
| 387 | if ($error && index($error, 'in @INC') != -1 && $mro_initialized == 0) { | ||||
| 388 | _warn( | ||||
| 389 | $ddp, | ||||
| 390 | ($] < 5.009_005 ? 'MRO::Compat' : 'mro') . ' not found in @INC.' | ||||
| 391 | . ' Objects may display inaccurate/incomplete ISA and method list' | ||||
| 392 | ); | ||||
| 393 | } | ||||
| 394 | $mro_initialized = $error ? -1 : 1; | ||||
| 395 | } | ||||
| 396 | |||||
| 397 | sub _get_namespace { | ||||
| 398 | my ($class_name) = @_; | ||||
| 399 | my $namespace; | ||||
| 400 | { | ||||
| 401 | 2 | 110µs | 2 | 17µs | # spent 12µs (7+5) within Data::Printer::Common::BEGIN@401 which was called:
# once (7µs+5µs) by Data::Printer::BEGIN@3.28 at line 401 # spent 12µs making 1 call to Data::Printer::Common::BEGIN@401
# spent 5µs making 1 call to strict::unimport |
| 402 | $namespace = \%{ $class_name . '::' } | ||||
| 403 | } | ||||
| 404 | # before 5.10, stashes don't ever seem to drop to a refcount of zero, | ||||
| 405 | # so weakening them isn't helpful | ||||
| 406 | Scalar::Util::weaken($namespace) if $] >= 5.010; | ||||
| 407 | |||||
| 408 | return $namespace; | ||||
| 409 | } | ||||
| 410 | |||||
| 411 | sub _get_superclasses_for { | ||||
| 412 | my ($class_name) = @_; | ||||
| 413 | my $namespace = _get_namespace($class_name); | ||||
| 414 | my $res = _get_symbol($class_name, $namespace, 'ISA', 'ARRAY'); | ||||
| 415 | return @{ $res || [] }; | ||||
| 416 | } | ||||
| 417 | |||||
| 418 | sub _get_symbol { | ||||
| 419 | my ($class_name, $namespace, $symbol_name, $symbol_kind) = @_; | ||||
| 420 | |||||
| 421 | if (exists $namespace->{$symbol_name}) { | ||||
| 422 | my $entry_ref = \$namespace->{$symbol_name}; | ||||
| 423 | if (ref($entry_ref) eq 'GLOB') { | ||||
| 424 | return *{$entry_ref}{$symbol_kind}; | ||||
| 425 | } | ||||
| 426 | else { | ||||
| 427 | if ($symbol_kind eq 'CODE') { | ||||
| 428 | 2 | 48µs | 2 | 11µs | # spent 8µs (5+3) within Data::Printer::Common::BEGIN@428 which was called:
# once (5µs+3µs) by Data::Printer::BEGIN@3.28 at line 428 # spent 8µs making 1 call to Data::Printer::Common::BEGIN@428
# spent 3µs making 1 call to strict::unimport |
| 429 | return \&{ $class_name . '::' . $symbol_name }; | ||||
| 430 | } | ||||
| 431 | } | ||||
| 432 | } | ||||
| 433 | return; | ||||
| 434 | } | ||||
| 435 | |||||
| 436 | 1 | 2µs | 1; |