| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Acme::Signature::Arity; | 
| 2 |  |  |  |  |  |  | # ABSTRACT: find out how a piece of code expects to be called | 
| 3 |  |  |  |  |  |  |  | 
| 4 | 1 |  |  | 1 |  | 73487 | use strict; | 
|  | 1 |  |  |  |  | 12 |  | 
|  | 1 |  |  |  |  | 29 |  | 
| 5 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 43 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.002'; | 
| 8 |  |  |  |  |  |  | our $AUTHORITY; | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 1 |  |  | 1 |  | 6 | use B; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 39 |  | 
| 11 | 1 |  |  | 1 |  | 5 | use List::Util qw(min); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 102 |  | 
| 12 | 1 |  |  | 1 |  | 7 | use experimental qw(signatures); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 | 1 |  |  | 1 |  | 609 | use parent qw(Exporter); | 
|  | 1 |  |  |  |  | 291 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | =head1 NAME | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | Acme::Signature::Arity - provides reliable, production-ready signature introspection | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | You'll know if you need this. | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | If you're just curious, perhaps start with L. | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | No part of this is expected to work in any way when given a sub that has a prototype. | 
| 27 |  |  |  |  |  |  | There are other tools for those: L. | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | For subs that don't have a prototype, this is I not expected to work. It might help | 
| 30 |  |  |  |  |  |  | demonstrate where to look if you wanted to write something proper, though. | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | =cut | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | our @EXPORT_OK = qw(arity min_arity max_arity coderef_ignoring_extra); | 
| 35 |  |  |  |  |  |  | our @EXPORT = qw(min_arity max_arity); | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | =head1 Exported functions | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | =head2 arity | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | Returns the C details for the first opcode for a coderef CV. | 
| 42 |  |  |  |  |  |  | If that code uses signatures, this might give you some internal details | 
| 43 |  |  |  |  |  |  | which mean something about the expected parameters. | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | Expected return information, as a list: | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | =over 4 | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | =item * number of required scalar parameters | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | =item * number of optional scalar parameters (probably because there are defaults) | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | =item * a character representing the slurping behaviour, might be '@' or '%', or nothing (undef?) if it's | 
| 54 |  |  |  |  |  |  | just a fixed list of scalar parameters | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =back | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | This can also throw exceptions. That should only happen if you give it something that isn't | 
| 59 |  |  |  |  |  |  | a coderef, or if internals change enough that the entirely-unjustified assumptions made by | 
| 60 |  |  |  |  |  |  | this module are somehow no longer valid. Maybe they never were in the first place. | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | =cut | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 17 |  |  | 17 | 1 | 24 | sub arity ($code) { | 
|  | 17 |  |  |  |  | 25 |  | 
|  | 17 |  |  |  |  | 24 |  | 
| 65 | 17 | 50 |  |  |  | 51 | die 'only works on coderefs' unless ref($code) eq 'CODE'; | 
| 66 | 17 |  |  |  |  | 59 | my $cv = B::svref_2object($code); | 
| 67 | 17 | 50 |  |  |  | 77 | die 'probably not a coderef' unless $cv->isa('B::CV'); | 
| 68 | 17 |  |  |  |  | 100 | my $next = $cv->START->next; | 
| 69 |  |  |  |  |  |  | # we pretend sub { } is sub (@) { }, for convenience | 
| 70 | 17 | 100 | 66 |  |  | 117 | return (0, 0, '@') unless $next and $next->isa('B::UNOP_AUX'); | 
| 71 | 15 |  |  |  |  | 85 | return $next->aux_list($cv); | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | =head2 max_arity | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | Takes a coderef, returns a number or C. | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | If the code uses signatures, this tells you how many parameters you could | 
| 79 |  |  |  |  |  |  | pass when calling before it complains - C means unlimited. | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | Should also work when there are no signatures, just gives C again. | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | =cut | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 8 |  |  | 8 | 1 | 5072 | sub max_arity ($code) { | 
|  | 8 |  |  |  |  | 12 |  | 
|  | 8 |  |  |  |  | 13 |  | 
| 86 | 8 |  |  |  |  | 18 | my ($scalars, $optional, $slurp) = arity($code); | 
| 87 | 8 | 100 |  |  |  | 26 | return undef if $slurp; | 
| 88 | 3 |  |  |  |  | 8 | return $scalars | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | =head2 min_arity | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | Takes a coderef, returns a number or C. | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | If the code uses signatures, this tells you how many parameters you need to | 
| 96 |  |  |  |  |  |  | pass when calling - 0 means that no parameters are required. | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | Should also work when there are no signatures, returning 0 in that case. | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | =cut | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 8 |  |  | 8 | 1 | 21012 | sub min_arity ($code) { | 
|  | 8 |  |  |  |  | 15 |  | 
|  | 8 |  |  |  |  | 12 |  | 
| 103 | 8 |  |  |  |  | 19 | my ($scalars, $optional, $slurp) = arity($code); | 
| 104 | 8 |  |  |  |  | 26 | return $scalars - $optional; | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | =head2 coderef_ignoring_extra | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | Given a coderef, returns a coderef (either the original or wrapped) | 
| 110 |  |  |  |  |  |  | which won't complain if you try to pass more parameters than it was expecting. | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | This is intended for library authors in situations like this: | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | $useful_library->each(sub ($item) { say "item here: $item" }); | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | where you later want to add optional new parameters, and don't trust your users | 
| 117 |  |  |  |  |  |  | to include the mandatory C<< , @ >> signature definition that indicates excess | 
| 118 |  |  |  |  |  |  | parameters can be dropped. | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | Usage - let's say your first library version looked like this: | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub each ($self, $callback) { | 
| 123 |  |  |  |  |  |  | my $code = $callback; | 
| 124 |  |  |  |  |  |  | for my $item ($self->{items}->@*) { | 
| 125 |  |  |  |  |  |  | $code->($item); | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | and you later want to pass the index as an extra parameter, without breaking existing code | 
| 130 |  |  |  |  |  |  | that assumed there would only ever be one callback parameter... | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | sub each ($self, $callback) { | 
| 133 |  |  |  |  |  |  | my $code = coderef_ignoring_extra($callback); | 
| 134 |  |  |  |  |  |  | for my $idx (0..$#{$self->{items}}) { | 
| 135 |  |  |  |  |  |  | $code->($self->{items}{$idx}, $idx); | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | Your library is now at least somewhat backwards-compatible, without sacrificing too | 
| 140 |  |  |  |  |  |  | many signature-related arity checking features: code expecting the new version | 
| 141 |  |  |  |  |  |  | will still complain if required parameters are not provided. | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | =cut | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 1 |  |  | 1 | 1 | 1879 | sub coderef_ignoring_extra ($code) { | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 2 |  | 
| 146 | 1 |  |  |  |  | 4 | my ($scalars, $optional, $slurp) = arity($code); | 
| 147 |  |  |  |  |  |  | # If we're accepting unlimited parameters, no need to do any more work | 
| 148 | 1 | 50 |  |  |  | 5 | return $code if $slurp; | 
| 149 |  |  |  |  |  |  |  | 
| 150 | 1 |  |  |  |  | 3 | my $max_index = $scalars - 1; | 
| 151 | 1 |  |  | 1 |  | 2 | return sub (@args) { | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 152 |  |  |  |  |  |  | # Some parameters may be optional, so we allow shorter lists as well | 
| 153 | 1 | 50 |  |  |  | 14 | $code->(@args ? @args[0 .. min($#args, $max_index)] : ()); | 
| 154 |  |  |  |  |  |  | } | 
| 155 | 1 |  |  |  |  | 8 | } | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | 1; | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | __END__ |