moderated >> Why does "use base" grow the call stack exponentially?

by Heini » Sat, 12 Feb 2005 02:47:13 GMT

I was trying to find out a way for a parent class to register its
subclasses, and made experiments with require. While the result was
succesful, in that I did find a way to register the subclasses, it also
gave me a great surprise.

What on earth is going on when the call stack grows exponentially here?

I have a parent class:

package abc;
sub xyz {
my $self = shift;
my $caller = caller;
for ( my $i = 1; $caller; $i++ ) {
warn ( "caller $i in $self: $caller" .
( $caller->isa( $self ) ? " (child)" : "" ) .
"\n" );
( $caller ) = caller($i);
}
warn "\n";
1;
}
__PACKAGE__->xyz;


and a child class:

package cde;
use base qw(abc);
__PACKAGE__->xyz;

and a grand child:

package efg;
use base qw(cde);
__PACKAGE__->xyz;


and here comes the surprise:

$ perl -e 'use efg'
caller 1 in abc: abc (child)
caller 2 in abc: base
caller 3 in abc: base
caller 4 in abc: cde (child)
caller 5 in abc: main
caller 6 in abc: main
caller 7 in abc: base
caller 8 in abc: base
caller 9 in abc: efg (child)
caller 10 in abc: main
caller 11 in abc: main
caller 12 in abc: main
caller 13 in abc: main
caller 14 in abc: main

caller 1 in cde: cde (child)
caller 2 in cde: base
caller 3 in cde: base
caller 4 in cde: efg (child)
caller 5 in cde: main
caller 6 in cde: main
caller 7 in cde: main
caller 8 in cde: main
caller 9 in cde: main

caller 1 in efg: efg (child)
caller 2 in efg: main
caller 3 in efg: main
caller 4 in efg: main

Heini

moderated >> Why does "use base" grow the call stack exponentially?

by peter » Sat, 12 Feb 2005 08:32:12 GMT


In article < XXXX@XXXXX.COM >,
Heini < XXXX@XXXXX.COM > writes:
[snip]
[snip]

There's an eval() in base.pm creating an extra calling scope.
Alter your subroutine xyz to:

sub xyz {
my $self = shift;
for ( my $i = 0; my ($pack, $sub) = (caller($i))[0,3]; $i++ ) {
warn ( "caller $i in $self: $pack" .
( $pack->isa( $self ) ? " (child)" : "" ) .
" ($sub)\n" );
}
warn "\n";
}

and see the output now:

caller 0 in abc: abc (child) (abc::xyz)
caller 1 in abc: base ((eval))
caller 2 in abc: base ((eval))
caller 3 in abc: cde (base::import)
caller 4 in abc: main (cde::BEGIN)
caller 5 in abc: main ((eval))
caller 6 in abc: base ((eval))
caller 7 in abc: base ((eval))
caller 8 in abc: efg (base::import)
caller 9 in abc: main (efg::BEGIN)
caller 10 in abc: main ((eval))
caller 11 in abc: main ((eval))
caller 12 in abc: main (main::BEGIN)
caller 13 in abc: main ((eval))

caller 0 in cde: cde (child) (abc::xyz)
caller 1 in cde: base ((eval))
caller 2 in cde: base ((eval))
caller 3 in cde: efg (base::import)
caller 4 in cde: main (efg::BEGIN)
caller 5 in cde: main ((eval))
caller 6 in cde: main ((eval))
caller 7 in cde: main (main::BEGIN)
caller 8 in cde: main ((eval))

caller 0 in efg: efg (child) (abc::xyz)
caller 1 in efg: main ((eval))
caller 2 in efg: main (main::BEGIN)
caller 3 in efg: main ((eval))

--
Peter Scott
http://www.perldebugged.com/
*** NEW *** http://www.perlmedic.com/

moderated >> Why does "use base" grow the call stack exponentially?

by Heini » Sun, 13 Feb 2005 01:56:08 GMT


I realized this right after sending the message. And in fact, also my
joy for finding a solution was premature. The output I saw was only from
one branch of the inheritance tree, and that it happened to list all
classes was a coincidence, due to my poor test data.

I am still trying to find a way for a parent class to somehow get a hold
of its subclasses, at the time they "use base" or "require" the parent.

The closest I have been able to come up with is to implement an import
method, which checks if the caller is a subclass. But this breaks when
the subclass implements its ow import, and also when nobody calls import
on the subclass or it doesn't get called early enough for the parent to
be able to affect the subclass the way I want.

Because I cannot do this, I have to ask the subclass developers to add a
definition to the subclasses. That is what I am trying to avoid.

Is there any hope at all?

Heini

moderated >> Why does "use base" grow the call stack exponentially?

by Adrian Howard » Sun, 13 Feb 2005 05:09:57 GMT


[snip]
[snip]

I missed the start of the thread so this may not be what you're after...

However, it sounds like you can get what you want by running an INIT
block in your parent class that looks for the sub-classes and does
whatever you want to them. Something like this in the parent should do
it:

INIT {
do_something_to_subclass( $_ )
foreach ( grep { $_ && $_->isa( __PACKAGE __ ) }
Devel::Symdump->rnew->packages;
}

?

(this won't, of course, work for sub-classes created after INIT has run)

Adrian

moderated >> Why does "use base" grow the call stack exponentially?

by Yitzchak Scott-Thoennes » Mon, 14 Feb 2005 01:49:49 GMT


Not much you can do about require except globally intrusive things like
coderef-in-@INC or overriding CORE::GLOBAL::require.

For a normal "use", you can use import in your superclass. I'm not
sure what the breakage you refer to below is, since the subclass
having or not having an import should have nothing to do with it.

Even though "use base" doesn't call import, it does test to see if
a module is loaded by accessing it's $VERSION, so you can detect
subclassing via base by tying your superclass's $VERSION, something
like:

package Super;
use strict;
use warnings;
our $VERSION = "1.00";
tie $VERSION, "Super::versiontrap", "$VERSION";

sub import {
return if $_[0] ne __PACKAGE__;
my $caller = caller;
if ($caller->isa(__PACKAGE__)) {
print "subclassed by $caller!\n";
} else {
# @ISA may not be set until after use...
print "Possible attempt to subclass by ", scalar(caller), "!\n";
}
}

package Super::versiontrap;
sub TIESCALAR { my $v = $_[1]; bless \$v }
sub FETCH {
# caller(1) works with current versions of base...a better way
# would be to look up the call stack for the first non-base entry
print "subclassed by ", scalar(caller(1)), "!\n" if caller eq "base";
${$_[0]}
}
sub STORE { ${$_[0]} = $_[1] }
1;


I wouldn't recommend using INIT to detect subclasses, because it will
not work if your module is only loaded after compilation is complete
(where your module or a subclass is require'd by the main script.)

moderated >> Why does "use base" grow the call stack exponentially?

by Heini » Mon, 14 Feb 2005 15:29:08 GMT


Where do you fnd this information? I cannot find it anywhere.


Wow, I wish that would work, but it doesn't - the trap doesn't snap, no
matter how much I try. Maybe it's because I am using Perl 5.6.1? I have
to write all my code to be runnable with it, so I haven't even tried any
later version yet.
However, the basic idea, that of trying to trap what "use base" does,
sounds workable. How can I find out what method "use base" in Perl 5.6.1
uses to check if a module is already loaded? I've spent quite some time
now trying to look for documentation about it, but got nil results.

What I am trying to do is to add a one-liner to the subclasses. As it is
just a one-liner, it doesn't sound like much of a burden to ask for the
subclass developers to do, but this is a matter of principle, serving
the user as best as I can.

This one-liner could easily be push-installed to the subclasses by using
eval:

sub AUTOLOAD { goto &{ __PACKAGE__->SUPER_AUTOLOAD } }

I won't explain that because the full story would be too lengthy. If it
bothers you, take a a look at the Shell module and try to figure out why
the AUTOLOAD routine in it conditionally throws away its first argument
- and why the import in it always throws away the first arg.

Heini

moderated >> Why does "use base" grow the call stack exponentially?

by John Bokma » Tue, 15 Feb 2005 04:00:57 GMT


%INC ?

--
John MexIT: http://johnbokma.com/mexit/
personal page: http://johnbokma.com/
Experienced programmer available: http://castleamber.com/
Happy Customers: http://castleamber.com/testimonials.html

moderated >> Why does "use base" grow the call stack exponentially?

by Heini » Tue, 15 Feb 2005 20:25:08 GMT


Sorry, I did find it documented, after all. I cannot see why I
didn't see it right away.

Anyway, the way the base pragma reads the variable does not "FETCH" it,
and the trap doesn't snap, not at "use base". I can make it snap by
using the variable myself, but then again I have the problem of adding
that to the subclasses. Doing it in the parent class does not help.

Sigh. What else does the base pragma do that possibly could be trapped?

I am using ActiveState Perl 5.6.1, and I have to write my code so that
it works with build 629, and I cannot expect the base pragma or any
other module to be upgraded in order for my script to work right.

I am about to give up.

Heini

----- End forwarded message -----

Similar Threads

1. tracing SQL calls with a stack dump - Perl DBI

2. trace sub's calls stack to STDERR (for cgi debugging)

Hi,

I am developing a web application framework (based on template toolkit).
Its design is Object Oriented, so a lot of
  $self->foobar();
kind of methods are called.

I'd like to trace these calls to a log file or STDERR (with Carp perhaps?)
so they get to Apache's error log.

All my classes are subclasses of a Object.pm package, so if I can change the
method invocation there to write a debug statement, I am all set.

Any hints ?

TIA

henq





3. printing out the values of variables in a previous stack frame using perl debugger

4. Insideous non-breaking space, why can't it be called a whitespace \s and be done with it?

I'm not sure how the below sample will look under your newsreader.
I am using Agent. I usually don't look at raw nntp messages.
I made the mistake of cut n' pasting a sample picked up on this
group, into Notepad to try it out. It should have worked but didn't.
Unfortunately, the sample was from Google news reader.

As I went back and toggled Agent into 'raw', the =A0 showed up of
course. Interrestingly, Notepad and Word shows it as A0 & 7f = 20 = ' '.

This didn't bother me too much but got me thinking.
All my instincts are telling me not to ask this because A0 > 7F,
and A0 is distinct in meaning but here goes.

As far as I know, Perl has no significant meaning for A0,
its just another character.
Just for this specific character, why can't it be a whitespace
at least in context of regex's and possibly when the source is
parsed?

As it is now, parsed regex's allow embedded A0, but all other
places generate "unknown character in source" error.
And I'm not sure that a Word edited source cannot contain
embedded non-breaking spaces.

End of stupid question..

-sln
-----------
use strict;
use warnings;

my @strings = (' ', ' 	  ');
for my $str (@strings)
{
	print "\nstring = '$str'\n";
	print "all whitespaces\n" if ($str =~ /^\s+$/);
	for (map {ord $_} split //,$str) {
		printf "%02x & 0x7f = %02x\n", $_, ($_ & 0x7f);
	}
}
print "\n";

if ( "some_text" =~ /
  ^ # begin string
    .*  # anything or nothing
  $     # end string
  /x )
{ print "matched\n" }
   else
{ print "didn't match anything/nothing\n" }

print "\n";

if ( "some_text" =~ /
  ^     # begin string
    .*  # anything or nothing
  $     # end string
  /x )
{ print "matched\n" }
   else
{ print "didn't match anything/nothing\n" }

__END__

Output:

c:\>emp>perl jj.pl

string = ' '
20 & 0x7f = 20
a0 & 0x7f = 20
20 & 0x7f = 20
a0 & 0x7f = 20
20 & 0x7f = 20

string = '        '
all whitespaces
20 & 0x7f = 20
09 & 0x7f = 09
20 & 0x7f = 20
20 & 0x7f = 20

didn't match anything/nothing

matched

c>\temp>

5. Why isn't Tk804.025 called Tk844.025?

6. Why does dir produce different results when called from Perl

That is, why does the output differ between:

perl -e "print join(\"\n\",@{[`dir`]});"

and

dir | perl -pne ""




7. Need help in using Base 64 encoding decoding for imagesu - Perl

8. Web based admin using PERL and CGI

Hey all, I am thinking I'd like to "browserize" a number of simple functions, please correct me if I understand what I'd need to do.  Lets say I want to run a pkginfo command on my web server, and return teh results to a browser, I'd need to call a ksh script from a CGI module?  So I'd make a form in perl, where I could button select which packages I wanted to see, then pass those package names so in effect on the back end I could do a pkginfo |grep <FROM_WEB_FORM>
So I can write the ksh easy, my question is how to call it and return the results I guess.  I am sure this is all documented, and I know there's been a lot of talk of calling seperate programs from perl, does this fit?  Can someone point me in the right direction for documentation on how to do this.  Basically I need to be able to call ksh scripts and pass them operands, and parse the results into a list/form for a browser.
Thanks,
Jason