Re: goto &Package::func destroying @_?!

From: Matthew Braid (not_at_invalid.invalid)
Date: 03/07/05


Date: Mon, 07 Mar 2005 11:46:52 +1000

Anno Siegel wrote:
> Matthew Braid <not@invalid.invalid> wrote in comp.lang.perl.misc:
>>Unfortunately this is another one that I can't reproduce in a small
>>script, but maybe the description will trigger something from someone....
>>
>>In the system I'm writing library functions can be extended like so:
>>
>>package Core;
>>use base qw/Exporter/;
>
> Why are you making Exporter a base class? You're not using it.

Hmm, good point - probably an artifact of development. In the original
code the 'Core' package uses another package as a base that has a custom
import function.

>>{
>> no strict 'refs';
>> sub import { # This code severely chopped down to basics
>> # Redefine import so that overrides are noticed even
>> # in places where the original function has already been
>> # imported
>> my $class = shift;
>> my @funcs = @_;
>> my ($caller) = caller();
>> for my $f (@funcs) {
>> *{"$caller\::$f"} = sub {goto &{"$class\::$f"}};
>
>
> A sub that does nothing but "goto &foo" is equivalent to foo. As given,
> you might as well say
>
> *{"$caller\::$f"} = \ &{"$class\::$f"};

Not quite. In my way, if "$class\::$f" is overridden later to a new
function, the new function will be called even in packages where
"$class\::$f" has _already been imported_. The code below demonstrates this:

# START CODE
{
  package Foo;
  sub test {
    print "FOO!\n";
  }
}

{
   package Bar;
   *Bar::test = \&Foo::test; # Standard import
   sub bartest {
     print "BAR!\n";
     test();
   }
}
{
   package Woo;
   # V---- My import - changes will be noticed
   BEGIN {*Woo::test = sub { goto &Foo::test }}
   sub wootest {
     print "WOO!\n";
     test();
   }
}

*test = \&Foo::test;
*bartest = \&Bar::bartest;
*wootest = \&Woo::wootest;
test(); # CALL 1
bartest(); # CALL 2
wootest(); # CALL 3
*Foo::test = sub { print "NEW FOO!\n" };
test(); # CALL 4
bartest(); # CALL 5
wootest(); # CALL 6
# END CODE

Which results in (comments added):

FOO! # CALL 1
BAR! # CALL 2
FOO!
WOO! # CALL 3
FOO!
FOO! # CALL 4 - changes not noticed
BAR! # CALL 5
FOO! # Hasn't noticed Foo::test has changed!
WOO! # CALL 6
NEW FOO! # _Has_ noticed Foo::test has changed!

Of course, in my actual code there wouldn't be three names - 'bartest'
and 'wootest' would all be called 'test' so the main script doesn't need
to know the new function names. Not usually what you want (which is why
Exporter does it your way) but in my case it is.

>>sub foo {
>> print "Core::foo GOT (@_)\n";
>>}
>># --- In a file not so very far away...
>>package Extension;
>>BEGIN {
>> require Core;
>> my $old = \&Core::foo;
>> sub foo {
>> print "Ext::foo GOT (@_)\n";
>> goto &$old;
>> }
>> *Core::foo = \&foo;
>
> Here you are changing the package Core. Normally an importing module
> has no business doing that.

True, but as I said the importing module is acting as a plugin that is
_supposed_ to override the 'Core'. Its whole purpose is to do 'extra
stuff' without the main script needing to do anything different. So
rather than the main script needing to be changed for every plugin, all
it needs to do is 'eval "require $extension"' (with error checking and
whatnot) - and then extensions can be defined in a config file.

>>Of course, extensions can be extended etc etc. The problem I've run in
>>to is the goto bit - it seems that if the level of 'extension' is too
>
> Which goto? There is one in Core and one in Extension.

So far I've only run across this in the extensions. In the right
circumstances I guess the core goto's would also suffer the same effects
if it really is a problem with goto&.

>>high, the destination of the goto ends up with @_ full of undefs - right
>>number of args, wrong values.
>
> How high is too high? With your code the arguments are handed down
> just fine.

In my real code the level is 4 goto&'s. I know extending the test code I
posted does work - hence the first line of my post about it being one of
those annoying problems that I can't easily replicate. I spent a very
frustrating two hours trying to get it to happen in test code, and I
don't think my company would be too happy about me posting what is
technically their code to a newsgroup. It all boiled down to three
debugging lines:

print "EXT3: ABOUT TO GOTO EXT2::func - \@_ IS (@_)\n";
goto &Ext2::func;

#... in another package not so far away

package Ext2;
#....
sub func {
print "EXT2: ARRIVED AT EXT2::func - \@_ IS (@_)\n";
        my ($href, @otherstuff) = @_;
print "EXT2::func: ABOUT TO USE KEY METHOD...\n";
        $href->{KEY}->method();
print "EXT2::func: DONE!\n";
}

which resulted in:

EXT3: ABOUT TO GOTO EXT2::func - @_ IS (HASH(0x804d170) ARRAY(0x804d260) 3)
EXT2: ARRIVED AT EXT2::func - @_ IS ( )
EXT2::func: ABOUT TO USE KEY METHOD...
pid 37031 (perl), uid 303: exited on signal 11

> Signal numbers are system-dependent. Is that a SEGV?

This is on FreeBSD, so yeah a segfault.

>>Oddly enough, if I throw in the otherwise useless line:
>>
>> @_ = @_;
>>
>>just before the goto, suddenly it works again.
>>
>>So there is a workaround - either the odd '@_ = @_' thing which looks
>>too much like a smiley or instead of goto& just simply call the old
>>function as in $old->(@_), but I'm thinking the destruction of @_ and
>>the terminate on signal 11 thing may be a bug in perl itself.
>
> We (or anyone else concerned, like p5p) would have to see the effect.
> Your code doesn't show it.

I'm trying :) Its the useless-use-of-@_=@_ that makes me think it must
be something happening between chained goto's - somehow the link to @_
is being weakened to a point where the next goto ends up pointing to the
wrong peice of memory and segfaults. Since including the broken @_ in a
string still 'works' (ie doesn't cause a segfault) I'm guessing maybe a
screwed up internal perl structure that can't be dereferenced but can be
stringified? The 'bad' @_ stringifies as if it was an array filled with
empty strings.

I wasn't posting this with the intention of someone being able to fix it
on this information - obviously its not enough. I was hoping someone
with a bigger perl head than me could see the situation and maybe give
me some tips on how to try and replicate it for further debugging. The
main code has been running fine for some time - it was just a new
extension that must have gone just over the line that suddenly threw
everything out.

MB



Relevant Pages

  • Re: goto &Package::func destroying @_?!
    ... > script, but maybe the description will trigger something from someone.... ... A sub that does nothing but "goto &foo" is equivalent to foo. ... There is one in Core and one in Extension. ...
    (comp.lang.perl.misc)
  • Re: package require "invalid argument" error
    ... find out what versions of "foo" are known to the package system ... Look through those versions and determine which one Tcl is trying to ... That will return the script which is ...
    (comp.lang.tcl)
  • RE: Calling subroutines from another file
    ... main script and the package need to use. ... sub foo { ... Read perlmod for more info on writing modules. ...
    (perl.beginners)
  • Re: tcl /tk on cygwin + mouse wheel event
    ... "" foo ... invoque the same script but directly on bash: ... I tried 'package require tk' but didn't make any change. ... invoque alone does it load some package I am not aware of? ...
    (comp.lang.tcl)
  • Re: from __future__ import absolute_import ?
    ... foo not in bar ... A path below the package level is generally a good means to shoot ... to represent a python "package" structure. ...
    (comp.lang.python)