Why I can get subroutine address before it is declared without error?
Asked Answered
E

2

6

I have next program:

use warnings;
use strict;

BEGIN {
    print \&mysub;
}


sub mysub {};

print \&mysub;

Its output:

CODE(0x118e890)CODE(0x118e890)

The BEGIN block is processed in compile time. At that point definition of sub mysub is not seen by compiler yet. But program still prints right subroutine address, which it will have when defined.

Why I do not get error here? Is this some sort of autovivification?

Economist answered 4/7, 2017 at 12:22 Comment(2)
You can remove the last two lines, and it works. You can also remove BEGIN {} and it still prints the address and no warnings or errors. However if you try to call the sub afterwards (without defining it), you get Undefined subroutine &main::mysub, so it is not autovivified. Seems like a bug.Milena
What that does is create a subroutine 'stub'. Essentially it does enough to allow the code to proceed. As was mentioned, you can not actually call the sub. What will break this code is if you attempt to use prototypes. It wouldn't be able to understand the argument list, so the code would actually break.Bureau
C
4

Yes, this is a form of autovivification. A stub is created when a reference to the sub is required and the sub doesn't exist.

use strict;
use warnings qw( all );
use feature qw( say );

sub test {
   say  defined(&mysub) ? "defined (".\&mysub.")"
      : exists(&mysub)  ? "exists (".\&mysub.")"
      :                   "doesn't exist";
}

test();
my $ref = \&mysub;
test();
eval("sub mysub { }  1") or die($@);
test();

Output:

doesn't exist
exists (CODE(0xab8cd8))
defined (CODE(0xab8cd8))
Coauthor answered 4/7, 2017 at 16:57 Comment(2)
Why this exists( *{ main::mysub }{ CODE } ) does not work?Economist
exists requires a hash element of &something. Besides, every slot of a glob slot always exists (since it's a C struct), so checking for existance is not useful. You can check if the slot contains something by checking it's defined (or true).Coauthor
F
2

Very interesting question. I'm writing this as an answer instead of a comment because it will be rather long, but there are still some bits I'm not entirely sure about.

I believe your intuition is correct and that it is a form of autovivification.

Devel::Peek can spread more light on what's happening.

I changed your code a little bit:

use warnings;
use strict;
use Devel::Peek;

$|++;

BEGIN {
  Dump( \&mysub );
  print \&mysub;
};

sub mysub {};

Dump( \&mysub );
print \&mysub;

I added $|++ so that buffering won't be cause of confusions, and added calls to Devel::Peek::Dump to look into the reference \&mysub. Here the output on my system:

SV = IV(0x2628628) at 0x2628638
  REFCNT = 1
  FLAGS = (TEMP,ROK)
  RV = 0x26286e0
  SV = PVCV(0x2640750) at 0x26286e0
    REFCNT = 2
    FLAGS = (DYNFILE)
    COMP_STASH = 0x25ffdb0  "main"
    ROOT = 0x0
    GVGV::GV = 0x26287a0    "main" :: "mysub"
    FILE = "/tmp/autov.pl"
    DEPTH = 0
    FLAGS = 0x1000
    OUTSIDE_SEQ = 0
    PADLIST = 0x0
    OUTSIDE = 0x0 (null)
CODE(0x26286e0)SV = IV(0x25fff20) at 0x25fff30
  REFCNT = 1
  FLAGS = (TEMP,ROK)
  RV = 0x26286e0
  SV = PVCV(0x2640750) at 0x26286e0
    REFCNT = 2
    FLAGS = (DYNFILE)
    COMP_STASH = 0x25ffdb0  "main"
    START = 0x262ea50 ===> 1
    ROOT = 0x262ea10
    GVGV::GV = 0x26287a0    "main" :: "mysub"
    FILE = "/tmp/autov.pl"
    DEPTH = 0
    FLAGS = 0x1000
    OUTSIDE_SEQ = 371
    PADLIST = 0x2648620
    PADNAME = 0x2630180(0x2667f70) PAD = 0x2628770(0x262f020)
    OUTSIDE = 0x2600140 (MAIN)
CODE(0x26286e0)

Note how Dump's output changes between the two calls. The first time Dump is called we just have a reference to a empty scalar. The second time, after the actual definition of the subroutine, you can see the details that pertain to subroutines have been fleshed out: namely PADLIST (now not null), PADNAME and START (I'm not an expert of Perl guts but I think this is the actual "pointer" to the subroutine).

I hope this helps. I'd be interested in knowing what you'll discover if you'll dig deeper in the problem.

Fortner answered 4/7, 2017 at 13:10 Comment(1)
You might want to add an example of what happens if the subroutine has prototypes...Bureau

© 2022 - 2024 — McMap. All rights reserved.