Skip Menu |

This queue is for tickets about the Test-Weaken CPAN distribution.

Report information
The Basics
Id: 53857
Status: rejected
Priority: 0/
Queue: Test-Weaken

People
Owner: KRYDE [...] cpan.org
Requestors: user42 [...] zip.com.au
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: ignoring global coderefs
Date: Thu, 21 Jan 2010 08:26:21 +1100
To: bug-Test-Weaken [...] rt.cpan.org
From: Kevin Ryde <user42 [...] zip.com.au>
I think I've mentioned this before, but if I make a separate ticket it won't be lost. Now coderefs are tracked, it'd be good if there was a way to ignore global functions that appear in objects as callbacks etc, like \&Foo::Bar::somefunc, a package function expected to be permanent. my $obj = { callback_func => \&somefunc, ... }; The best could be to ignore as permanent any coderef which appears as a function in any package. But that would require searching through everything and could be slow and would want protection against crazy circular stashes (arising from aliasing, or botched aliasing!). I got to three alternatives below. The "sub foo {}" one is easiest to use, but it doesn't work on constructed "*foo=sub{}" things as they don't acquire a name from an assignment. The particular named functions one is the tightest, but it'd be bad to hard code names that a sub-object might use for private purposes. The whole-package approach is better for that, but still imperfect. # =item C<$bool = Test::Weaken::Gtk2::ignore_global_function ($ref)> # # Return true if C<$ref> is a coderef to a global function like C<sub foo {}>. # # A global is identified by the coderef having a name, and the current # function under that name equal to this coderef. Plain functions created # as C<sub foo {}> etc work, but redefinitions or function-creating modules # like C<Memoize> or C<constant> generally don't. # # For reference, the name in a coderef is basically just a string from its # original creation. C<Memoize> and similar end up with anonymous # functions, and C<constant> only ends up with a name under the scalar in # symtab optimization. # sub ignore_global_function { my ($ref) = @_; ref $ref eq 'CODE' or return; # could use Sub::Identify, but B comes with perl already require B; my $cv = B::svref_2object($ref); my $gv = $cv->GV; # as per Sub::Identify, for some sort of undefined GV return if $gv->isa('B::SPECIAL'); my $fullname = $gv->STASH->NAME . '::' . $gv->NAME; # Test::More::diag "ignore_global_function() fullname $fullname"; return (defined &$fullname && $ref == \&$fullname); } # =item C<$bool = ignore_function ($ref, $funcname, $funcname, ...)> # # Return true if C<$ref> is a coderef to any of the given named functions. # # Each C<$funcname> is a fully-qualified string like C<Foo::Bar::somefunc>. # If a function doesn't exist then it's skipped, so it doesn't matter if the # C<Foo::Bar> package is actually loaded yet, etc. # sub ignore_function { my $ref = shift; ref $ref eq 'CODE' or return; while (@_) { my $funcname = shift; if (defined &$funcname && $ref == \&$funcname) { return 1; } } return 0; } # =item C<$bool = ignore_module_functions ($ref, $module, $module, ...)> # # Return true if C<$ref> is a coderef to any function in any of the given # modules. # # Each C<$module> is a string like C<My::Module>. If a module doesn't exist # then it's skipped, so it doesn't matter if the C<My::Module> package is # actually loaded yet. # sub ignore_module_functions { my $ref = shift; ref $ref eq 'CODE' or return; while (@_) { my $module = shift; my $symtabname = "${module}::"; no strict 'refs'; defined %$symtabname or next; foreach my $name (keys %$symtabname) { my $fullname = "${module}::$name"; if (defined &$fullname && $ref == \&$fullname) { return 1; } } } return 0; }
Breaking out separate issues as separate tickets is IMHO a good idea. I'm still immersed in Marpa development, and will remain so for some weeks to come, so I'll have to ask for your patience on this one.
Don't think can reliably detect global coderefs just from their own nature. My code in Test::Weaken::ExtraBits are some possible approaches.