Wednesday, January 23, 2008

The Subroutine Stack

Whenever Perl calls a subroutine, it pushes the details of the subroutine call onto an internal stack. This holds the context of each subroutine, including the parameters that were passed to it in the form of the @_ array, ready to be restored when the call to the next subroutine returns. The number of subroutine calls that the program is currently in is known as the 'depth' of the stack. Calling subroutines are higher in the stack, and called subroutines are lower.

This might seem academic, and to a large extent it is, but Perl allows us to access the calling stack ourselves with the caller function. At any given point we are at the 'bottom' of the stack, and can look 'up' to see the contexts stored on the stack by our caller, its caller, and so on, all the way back to the top of the program. This can be handy for all kinds of reasons, but most especially for debugging.

In a purely scalar context, caller returns the name of the package from which the subroutine was called, and undef if there was no caller. Note that this does not require that the call came from inside another subroutine – it could just as easily be from the main program. In a list context, caller returns the package name, the source file, the line number from which we were called, and the name of the subroutine that was called (i.e. us). This allows us to write error traps in subroutines like:

sub mysub
($pkg, $file, $line) = caller;
die "Called with no parameters at $file line $line" unless @_;
}

If we pass a numeric argument to caller, it looks back up the stack the requested number of levels, and returns a longer list of information. This level can of course be '0', so to get everything that Perl knows about the circumstances surrounding the call to our subroutine we can write:

@caller_info = caller 0;   # or caller(0), if we prefer

This returns a whole slew of items into the list, which may or may not be defined depending on the circumstances. They are, in order:

  • package: the package of the caller.
  • filename: the source file of the caller.
  • line: the line number in the source file.
  • subroutine: the subroutine that was called (that is, us). If we execute code inside an eval statement then this is set to eval.
  • hasargs: this is true if parameters were passed (@_ was defined).
  • wantarray: the value of wantarray inside the caller, see 'Returning Values' later in the chapter.
  • evaltext: the text inside the eval that caused the subroutine to be called, if the subroutine was called by eval.
  • is_require: true if a require or use caused the eval.
  • hints: compilation details, internal use only.
  • bitmask: compilation details, internal use only.

In practice, only the first four items: package, filename, line, and subroutine are of any use to us, which is why they are the only ones returned when we use caller with no arguments. Unfortunately we do not get the name of the calling subroutine this way, so we have to extract that from further up the stack:

# get the name of the calling subroutine, if there was one
$callingsub = (caller 1)[3];

Or, more legibly:

($pkg, $file, $line, $callingsub) = caller 1;

Armed with this information, we can create more informative error messages that report errors with respect to the caller. For example:

# die with a better error message

sub mysub {
($pkg, $file, $line) = caller;
die "Called from ", (caller(1)) [3], " with no parameters at $file line $line \n" unless @_;
...
}

If debugging is our primary interest, a better solution than all the above is to use the Carp module. The Carp module and other debugging aids are covered in Chapter 17.

One final point about the calling stack: if we try to access the stack above the immediate caller we may not always get the right information back. This is because Perl can optimize the stack under some circumstances, removing intermediate levels. The result of this is that caller is not always as consistent as we might expect, so a little caution should be applied to its use.

5 comments:

Anonymous said...

Hello. This post is likeable, and your blog is very interesting, congratulations :-). I will add in my blogroll =). If possible gives a last there on my blog, it is about the Livros e Revistas, I hope you enjoy. The address is http://livros-e-revistas.blogspot.com. A hug.

Anonymous said...

Hi there! This post could not be written any better!
Reading this post reminds me of my old room mate! He
always kept chatting about this. I will forward this write-up
to him. Fairly certain he will have a good read.
Many thanks for sharing!

Also visit my weblog :: biggest loser diet

Anonymous said...

When I originally commented I clicked the "Notify me when new comments are added" checkbox and now each time
a comment is added I get several e-mails with the same comment.

Is there any way you can remove people from that service?
Appreciate it!

Check out my blog; ernährungspyramide download kostenlos
my page - ernährung steinzeit rezepte

Anonymous said...

My coder is trying to convince me to move to .net from PHP.
I have always disliked the idea because of the expenses.
But he's tryiong none the less. I've been using Movable-type on
a variety of websites for about a year and am concerned about switching to another
platform. I have heard good things about blogengine.
net. Is there a way I can transfer all my wordpress content into it?
Any kind of help would be really appreciated!

My web blog; online graduate certificate programs

help said...

orlando escorts The power of love big enough to make people forget everything, but so small that even a grain of the sand and envy also cannot accommodate。