Frankly, i would consider the use of a constant function (or other type of constant) for a port number a major bug in AnyEvent::DNS. There are very few use cases that make sense to declare a number a constant in programming code.
I assume you're not advocating for magic numbers - I might have said that a constant that isn't modifiable from outside the module is a problem.
I tried using source filters (Filter::Simple), but couldn't make it work
This was a fun project. I didn't exactly use source filters, but I did use PPI to munge the module before loading it, basically like a filtered use. Obviously this still has all the limitations of source filters and PPI!
Deconstifier.pm
package Deconstifier;
use warnings;
use strict;
use parent 'PPI::Transform';
=head1 DESCRIPTION
L<PPI::Transform> implementation that modifies a subset of
L<constant functions|perlsub/"Constant Functions"> such that they are
+no longer
inlined. The subset of C<sub> definitions that are currently supported
+ is:
sub FOO () { 42; }
sub BAR () { "string"; }
sub QUZ () { undef; }
where the final semicolon is optional.
=cut
sub document {
( my $self = shift )->isa(__PACKAGE__) or return undef;
( my $doc = shift )->isa('PPI::Document') or return undef;
my $subs = $doc->find(sub {
if ( $_[1]->isa('PPI::Statement::Sub') && defined($_[1]->proto
+type) && $_[1]->prototype eq "" ) {
my $bl = $_[1]->block;
if ( $bl && $bl->schildren==1 && $bl->schild(0)->isa('PPI:
+:Statement') ) {
my $st = $bl->schild(0);
if ( $st->schildren==1 || $st->schildren==2 && $st->sc
+hild(1)->isa('PPI::Token::Structure') && $st->schild(1)->content eq '
+;' ) {
my $ch = $st->schild(0);
if ( $ch->isa('PPI::Token::Number') || $ch->isa('P
+PI::Token::Quote') || $ch->isa('PPI::Token::Word') && $ch->literal eq
+ 'undef' ) {
return 1;
}
}
}
}
return 0;
});
return undef unless defined $subs;
return 0 unless $subs;
for my $s (@$subs) {
#use PPI::Dumper; PPI::Dumper->new($s, whitespace=>0, comments
+=>0)->print;
# This first one only seems to work on Perl 5.8+, the second d
+own to 5.6 and maybe/likely earlier (untested).
# NOTE: This isn't really the right way to use PPI::Token::Wor
+d, but since it's the only modification we're making it works fine.
#$s->block->schild(0)->schild(0)->insert_before(PPI::Token::Wo
+rd->new('return '));
$s->block->schild(0)->schild(0)->insert_after(PPI::Token::Word
+->new(' if $]'));
}
return 0+@$subs;
}
1;
deconstify.t
use warnings;
use strict;
use Test::More tests=>4;
BEGIN { use_ok 'Deconstifier' }
my $code = <<'END';
sub MAX_PKT() { 4096.0 }
sub DOMAIN_PORT() { 53; }
sub resolver ();
sub _enc_qd() {
(_enc_name $_->[0]) . pack "nn",
($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}),
($_->[3] > 0 ? $_->[2] : $class_id{$_->[2] || "in"})
}
sub _enc_rr() { die "encoding of resource records is not supported"; }
sub HELLO { "world" }
sub WORLD () { "foo" }
sub FOO () { $bar }
sub BAR () { return 123 }
sub BLAH () { undef; }
END
my $exp = <<'END';
sub MAX_PKT() { 4096.0 if $] }
sub DOMAIN_PORT() { 53 if $]; }
sub resolver ();
sub _enc_qd() {
(_enc_name $_->[0]) . pack "nn",
($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}),
($_->[3] > 0 ? $_->[2] : $class_id{$_->[2] || "in"})
}
sub _enc_rr() { die "encoding of resource records is not supported"; }
sub HELLO { "world" }
sub WORLD () { "foo" if $] }
sub FOO () { $bar }
sub BAR () { return 123 }
sub BLAH () { undef if $]; }
END
my $trans = new_ok 'Deconstifier';
ok $trans->apply(\$code), 'apply';
is $code, $exp, 'output is as expected';
FilterLoad.pm
package FilterLoad;
use warnings;
use strict;
use List::Util qw/ pairs pairkeys /;
use PPI;
use Module::Load::Conditional qw/ check_install /;
use Module::Runtime qw/ use_module module_notional_filename /;
=head1 DESCRIPTION
Loads modules after passing them through the L<PPI::Transform> filter(
+s) given
in the C<use> statement. For example:
use FilterLoad 'AnyEvent::DNS' => 'Deconstifier',
SomeModule => 'Deconstifier';
=cut
sub import {
my ($class, @defs) = @_;
my (%mods, %filts);
for ( pairs @defs ) {
my ($mod, $filt) = @$_;
$filts{$filt}++;
my $modfn = module_notional_filename($mod);
$mods{$modfn}{name} = $mod;
push @{ $mods{$modfn}{filts} }, $filt;
}
use_module($_) for keys %filts;
our $_in_inc_hook;
unshift @INC, sub {
my ($self, $modfn) = @_;
return if $_in_inc_hook;
return unless exists $mods{$modfn};
local $_in_inc_hook = 1; # check_install calls @INC hooks!
my $info = check_install(module=>$mods{$modfn}{name})
or die "could not find $modfn";
my $doc = PPI::Document->new($info->{file});
$_->new->apply($doc) for @{ $mods{$modfn}{filts} };
return \$doc->serialize;
};
use_module($_) for pairkeys @defs;
}
1;
test.pl
use warnings;
use strict;
use FilterLoad
Foo => 'Deconstifier',
'AnyEvent::DNS' => 'Deconstifier';
sub Foo::ONE () { 444 }
sub Foo::TWO { 555 }
sub Foo::THREE () { 666 }
Foo::go();
print "One=", Foo::ONE, ", Two=", Foo::TWO, ", Three=", Foo::THREE, "\
+n";
# I've manually edited the module to include a function
# sub foobar { print "DOMAIN_PORT=", DOMAIN_PORT, "\n" }
AnyEvent::DNS::foobar();
sub AnyEvent::DNS::DOMAIN_PORT () { 4242 }
AnyEvent::DNS::foobar();
Using Foo.pm from my node here, and modifying AnyEvent::DNS as noted in the code above, the output is:
Subroutine Foo::ONE redefined at test.pl line 9.
Subroutine Foo::TWO redefined at test.pl line 10.
Subroutine Foo::THREE redefined at test.pl line 11.
Subroutine AnyEvent::DNS::DOMAIN_PORT redefined at test.pl line 19.
One=444, Two=555, Three=666
One=444, Two=555, Three=666
DOMAIN_PORT=4242
DOMAIN_PORT=4242