Untangling subroutine attributes
Subroutine attributes are optional labels that can be included in a subroutine declaration. They’re a curious feature with a clunky interface and minimal documentation. They seem underused, but it’s hard to think of legitimate uses for them. In my opinion the coolest thing about subroutine attributes is that they run at compile time. This means you can execute custom code before the main program is run, and seeing as Perl gives you access to the symbol table, you can basically do wizardy things.
The lvalue trick
Perl has several subroutine attributes built-in. A useful one is lvalue
which tells Perl that the subroutine refers to a variable that persists beyond individual calls. A common case is using them as method getter/setters:
package Foo;
sub new { bless {}, shift }
sub bar :lvalue {
my $self = shift;
# must return the variable for lvalue-ness
$self->{bar};
}
package main;
my $foo = Foo->new();
$foo->bar = "dogma"; # not $foo->bar("dogma");
print $foo->bar;
By adding the attribute :lvalue
to the bar
subroutine, I can use it like a variable, getting, setting and substituting and so on.
Custom attributes
To use custom attributes in a package, you must provide a subroutine called MODIFY_CODE_ATTRIBUTES
. Perl will call this subroutine during compilation if it find any custom subroutine attributes. It’s called once for every subroutine with custom attributes. MODIFY_CODE_ATTRIBUTES
receives the package name, a coderef to the subroutine and a list of the attributes it declared:
package Sub::Attributes;
sub MODIFY_CODE_ATTRIBUTES {
my ($package, $coderef, @attributes) = @_;
return ();
}
sub _internal_function :Private {
...
}
1;
I’ve created a new package with the required subroutine - all it does is return an empty list. I’ve then declared an empty subroutine called _internal_function
which has a custom attribute, Private
. I want to do the impossible and create truly private subroutines in Perl by making any subroutine with the Private
attribute only callable by its own package. But what if I misspell Private
? If we received any attributes we didn’t recognize, MODIFY_CODE_ATTRIBUTES
can add them to a list and Perl will throw a compile time error:
package Sub::Attributes;
sub MODIFY_CODE_ATTRIBUTES {
my ($package, $coderef, @attributes, @disallowed) = @_;
push @disallowed, grep { $_ ne 'Private' } @attributes;
return @disallowed;
}
sub _internal_function :Private {
...
}
1;
I’ve updated to code to declare and return @disallowed
- an array of any unrecognized subroutine attributes. Even though it’s declared in the first line of the subroutine, it will always be empty because @attributes
gobbles up all remaining arguments passed to the subroutine. Next I grep through the list of attributes received and if any don’t match “Private”, I add them to the disallowed array.
Adding compile time behavior
Now any subroutine in the package can use the attribute Private
but it doesn’t do anything. I need to add some behavior!
package Sub::Attributes;
use B 'svref_2object';
sub MODIFY_CODE_ATTRIBUTES {
my ($package, $coderef, @attributes, @disallowed) = @_;
my $subroutine_name = svref_2object($coderef)->GV->NAME;
my %allowed = (
Private => sub {
my ($coderef, @args) = @_;
my ($calling_package, $filename, $line, $sub) = caller(2);
croak 'Only the object may call this sub' unless $sub && $sub =~ /^Sub\:\:Attributes\:\:/;
$coderef->(@args);
},
);
for my $attribute (@attributes) {
# parse the attribute into name and value
# attribute not known, compile error
push(@disallowed, $attribute) && next unless exists $allowed{$attribute};
# override subroutine with attribute coderef
my $overrider = $allowed{$attribute};
my $old_coderef = $coderef;
$coderef = sub { $overrider->($old_coderef, @_) };
*{"Sub:\:Attributes:\:$subroutine_name"} = $coderef;
}
return @disallowed;
}
sub _internal_function :Private {
...
}
sub call_internal_function {
_internal_function();
}
1;
This code imports the svref_2object
function from the B module. This handy function takes a reference and returns an object with the data from Perl’s internals. In this case, passing a coderef returns a B::CV object. I use this to get the subroutine name and overrride the subroutine later.
I’ve created a hash called %allowed
which is where I can declare any permitted custom attributes and their associated code. For Private
I made a coderef that checks the caller is in the same package and croaks if it’s not, else it will call it.
Next I loop through any attributes received, and check they exist in %attributes
. If they don’t, I push them into @disallowed
and skip to the next attribute. If the attribute does exist, I assign the coderef to $overrider
and declare a new coderef which will call $overrider
passing the old coderef to be called.
Finally I override the Private
subroutine with the new coderef:
*{"Sub:\:Attributes:\:$subroutine_name"} = $coderef;
This is how you override subroutines using a typeglob (Mastering Perl has a whole chapter dedicated to features like these, highly recommended). But what about that backslash in the middle of the colons :\:
?. That escape is necessary for the code to run on Perl versions 5.16 through 5.18 (thanks to Andreas König for debugging this).
If you’re wondering why I bothered to create $old_coderef
at all, it’s so that a subroutine can have multiple attributes with new behaviors nested inside each other.
Now any calls to _internal_function
will croak unless they come from within Sub::Attributes:
use Sub::Attributes;
Sub::Attributes::call_internal_function(); # ok
Sub::Attributes::_internal_function(); # croak!
Making it re-useable
If it seems dumb to create custom attributes and then elsewhere in the same code, validate those attributes, join the club. To get the most out of this system, you have to make your custom attributes re-usable. Fortunately, just a few changes are needed:
package Sub::Attributes;
use B 'svref_2object';
sub MODIFY_CODE_ATTRIBUTES {
my ($package, $coderef, @attributes, @disallowed) = @_;
my $subroutine_name = svref_2object($coderef)->GV->NAME;
my %allowed = (
Private =>
sub {
my $package = shift;
return sub {
my ($coderef, @args) = @_;
my ($calling_package, $filename, $line, $sub) = caller(2);
croak 'Only the object may call this sub' unless $sub && $sub =~ /^$package\:\:/;
$coderef->(@args);
}
},
);
for my $attribute (@attributes) {
# parse the attribute into name and value
# attribute not known, compile error
push(@disallowed, $attribute) && next unless exists $allowed{$attribute};
# execute compile time code
my $overrider = $allowed{$attribute}->($package);
next unless $overrider;
# override the subroutine if necessary
my $old_coderef = $coderef;
$coderef = sub { $overrider->($old_coderef, @_) };
*{"$package:\:$subroutine_name"} = $coderef;
}
$Sub::Attributes::attributes{$package}{$subroutine_name} = \@attributes;
return @disallowed;
};
1;
Rather than hardcoding the package name, I’ve made it dynamic. The key change here is that the coderef for Private
has been changed to a coderef that returns another coderef. Now I can execute some arbitrary code at compile time and optionally manufacture a new coderef that uses compile time information. In the case of Private
, I want to pass the package name of the private subroutine, so I can check later that the caller is from within the same package.
Why optionally return a coderef? Imagine if I created an attribute called After
which behaved like the after
function in Class::Method::Modifiers. In this case the subroutine with the private attribute would be reference a different subroutine. That might look like this:
sub foo { }
sub logger :After(foo) {
print "foo() was called!\n";
}
Here logger
should be executed after foo
. So logger itself never changes, and doesn’t need to be overridden.
I store the attributes for a subroutine under the package name in the symbol table for Sub::Attributes
. I could add them to the package’s symbol table, but I might inadvertently overwrite something else, so I keep the data within the Sub::Attributes
namespace.
$Sub::Attributes::attributes{$package}{$subroutine_name} = \@attributes;
Why no FETCH_CODE_ATTRIBUTES?
The attribute
docs mention another subroutine, called FETCH_CODE_ATTRIBUTES
that given a coderef, should return the attributes for the referenced subroutine. When attributes::get
is called, it passes the class of the declaring package, which is Sub::Attributes
:
# $class == "Sub::Attributes"
sub FETCH_CODE_ATTRIBUTES {
my ($class, $coderef) = @_;
my $cv = svref_2object($coderef);
# $class should be subclass name, not Sub::Attributes
return @{$Sub::Attributes::attributes{$class}{ $cv->GV->NAME }};
}
I don’t see a way to find out the package name of the original subroutine. FETCH_CODE_ATTRIBUTTES
is not required and if it’s not there Perl won’t throw an exception if attributes::get
is called. Instead I provided the sub_attributes
method which does work:
sub sub_attributes {
my ($package) = @_;
my $class_name = ref $package || $package;
return $Sub::Attributes::attributes{ $class_name };
}
This returns the attributes stored for a package. This might be useful if other packages want to inspect the attributes for a package’s subroutine. it can be called as an object method or class method:
package Foo;
use base 'Sub::Attributes';
...
Foo->sub_attributes(); # works
$foo->sub_attributes(); # works also
Squashing warnings
It’s generally good practice to use the strict
and warnings
pragmas to help detect issues with our code. However the code so far will emit some warnings and an exception if we add those pragmas as-is. This code will add the pragmas but make Perl ignore the violations:
use strict;
no strict 'refs';
use warnings;
no warnings qw(reserved redefine);
The reserved
warning is of particular interest here. This would be caused by using custom subroutine attributes, so no matter what, you’d want to turn that off. Redefine is a warning emitted whenever a subroutine is over-written, strict references means no interpolating of variable names in symbol table lookups; we need these features so we can dynamically patch subroutines like this:
*{"$class:\:$subroutine"} = $coderef
Making it extensible
If you’ve gone to the hard work of setting up the code for inheritable custom attributes, why not make it extensible? That way consuming packages can add their own custom attributes.
package Sub::Attributes;
use strict;
no strict 'refs';
use warnings;
no warnings qw(reserved redefine);
use B 'svref_2object';
BEGIN {
our %allowed = (
Private =>
sub {
my $package = shift;
return sub {
my ($coderef, @args) = @_;
my ($calling_package, $filename, $line, $sub) = caller(2);
croak 'Only the object may call this sub' unless $sub && $sub =~ /^$package\:\:/;
$coderef->(@args);
}
},
# compile time override, run a coderef after running the subroutine
After => sub {
my ($package, $value, $coderef) = @_;
# full name of the sub to override
my $fq_sub = "$package:\:$value";
my $target_coderef = \&{$fq_sub};
*{$fq_sub} = sub {
my @rv = $target_coderef->(@_);
$coderef->(@_);
return wantarray ? @rv : $rv[0];
};
# we didn't change the method with the attribute
# so we return undef as we have no runtime changes
return undef;
},
);
}
sub MODIFY_CODE_ATTRIBUTES {
my ($package, $coderef, @attributes, @disallowed) = @_;
my $subroutine_name = svref_2object($coderef)->GV->NAME;
for my $attribute (@attributes) {
# parse the attribute into name and value
my ($name, $value) = $attribute =~ qr/^ (\w+) (?:\((\S+?)\))? $/x;
# attribute not known, compile error
push(@disallowed, $name) && next unless exists $Sub::Attributes::allowed{$name};
# execute compile time code
my $overrider = $Sub::Attributes::allowed{$name}->($package, $value, $coderef);
next unless $overrider;
# override the subroutine if necessary
my $old_coderef = $coderef;
$coderef = sub { $overrider->($old_coderef, @_) };
*{"$package:\:$subroutine_name"} = $coderef;
}
$Sub::Attributes::attributes{$package}{$subroutine_name} = \@attributes;
return @disallowed;
};
sub sub_attributes {
my ($package) = @_;
my $class_name = ref $package || $package;
return $Sub::Attributes::attributes{ $class_name };
}
1;
I’ve moved the %allowed
hash into a BEGIN
block - this has to be declared at compile time so it’s available for MODIFY_CODE_ATTRIBUTES
. Now new custom attributes can be added by modifying %Sub::Attributes::attributes
. I also added a new custom attribute After
which implements causes the subroutine to be called after another one, like this:
sub foo { }
sub bar :After(foo) {
print "foo() was called!\n";
}
I added a regex which captures the attribute name and value when passing attributes (so for After(foo)
“After” is the name and “foo” is the value). The $value
and $coderef
are now passed to the custom attribute’s subroutine to allow compile-time overrides of other subroutines.
Resources
- attributes is the official documentation on attributes.
- Sub::Attributes is my module which implements the above code, and adds a few more custom attributes.
- perldata has an entry on typeglobs and the symbol table.
- Chapters 7 & 8 of Mastering Perl second edition cover the symbol table and overrriding subroutines in detail.
- perlsub has information on lvalue subroutines.
- Two useful blog posts by mascip on possible uses and when to use subroutine attributes.
- Attribute::Handlers provides a mechanism for calling subroutines via attributes.
This article was originally posted on PerlTricks.com.
Tags
David Farrell
David is a professional programmer who regularly tweets and blogs about code and the art of programming.
Browse their articles
Feedback
Something wrong with this article? Help us out by opening an issue or pull request on GitHub