Re: exists(&subname) causes strange autovivification problem
by LanX (Saint) on Nov 06, 2024 at 00:04 UTC
|
> I don't think exists(&subname) should "autovivify" anything.
Complex things are happening here, and I don't know in which order to untangle them
- a sub &name lives in a slot of the stash entry *name which is accessible via $main::{name} or $::{name} for short
- so if a sub is created, so is the glob autovivified
- exists might autovivify entries on higher level of the stash tree
- but this autovivification seems to already happen at compile time when the symbol &non is seen, even inside exists! Please note how $::{other} doesn't exist
- your use of no autovivification is changing what exists &sub returns, it's an empty list () instead of an empty string "" (both are false)
Compare my code experiments.
use strict;
use warnings;
use feature 'say';
no autovivification;
use Data::Dump qw/pp/;
my @ary;
pp exists $::{non};
pp exists $::{other};
pp exists (&non) ;
push @ary, exists (&non) ;
say scalar(@ary)
1 # *non exists after compilation of &non
"" # *other doesn't exist
() # sub &non doesn't exist but empty list returned
0 # @ary has consequently no elements
Edit
After commenting out #no autovivification
1
""
""
1
Update
My guess is that there is actually always an autovivification happening at compile time creating the glob, which has side effects on exists at run time.
I'd say this implementation is buggy!
If you need a workaround, I'd try to first checking if the glob exists before checking the slot and avoiding exists &name
| [reply] [d/l] [select] |
|
|
>
If you need a workaround, I'd try first checking if the glob exists before checking the slot and avoiding exists &name
I have no words to express how f*cked *p typeglobs are!!!
If only a sub is using the symbol, then the coderef is stored directly where the glob is supposed to be.
(Larry gets a spanking next time we meet ;)
Anyway this code seems to work in all edge cases, only tested on mobile phone yet.
use strict;
use warnings;
use feature 'say';
no autovivification;
use Data::Dump qw/pp/;
sub sub_exists{
my $name = shift;
return
exists $::{$name} # symbol exists
&&
(
ref $::{$name} eq "CODE" # only code
||
(*{$::{$name}}{CODE} && 1) # also code
||
!1 # false
);
}
for my $name (qw/zero one two three/) {
pp { $name => sub_exists($name) };
}
our $one=1;
our $two=2;
sub two {2};
sub three {3}
{ zero => "" }
{ one => "" }
{ two => 1 }
{ three => 1 }
TODO
Extend code to cover subs in other namespaces than main | [reply] [d/l] [select] |
|
|
sub sub_exists {
my $name = shift;
no strict qw( refs );
return exists( &$name );
}
Bonus: Not limited to subs in the current namespace.
To avoid the bug the OP identified, place the sub outside of scope of no autovivification;, or add use autovivification; to it.
sub sub_exists {
my $name = shift;
no strict qw( refs );
use autovivification; # Disable module to avoid bug.
return exists( &$name );
}
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
|
|
| [reply] |
Re: exists(&subname) causes strange autovivification problem
by ikegami (Patriarch) on Nov 06, 2024 at 17:37 UTC
|
$ t() {
perl -Mv5.40 -e'
no if $ARGV[0], "autovivification";
my @a;
push @a, [ exists(&nonesuch) ];
say $a[0][0] ? "exists" : "doesn\x27t exist";
' "$@"
}
$ t 0
doesn't exist
$ t 1
Modification of a read-only value attempted at -e line 4.
Workaround posted in a different post. | [reply] [d/l] |
|
|
> It's a bug in autovivification.
Reported.
map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
| [reply] [d/l] |
|
|
perl -E'no autovivification; push @a, [exists(&no)]; '
Modification of a read-only value attempted at -e line 1.
please note that the push is also needed to cause that error
as already explained, is exists returning an empty list - or something "similar" - for false in the buggy case.
perl -MData::Dump -E'dd exists(&non); no autovivification; dd exists(&
+non);'
""
()
| [reply] [d/l] [select] |
|
|
Can someone explain this syntax to me? I understand what it's doing but not why it's doing it.
no if $ARGV[0], "autovivification";
| [reply] [d/l] |
|
|
no if $ARGV[0], "autovivification";
no MODULE LIST; is like use MODULE LIST, but calls a different method (unimport(LIST) instead of import(LIST)) - see no
if is a module named if, not your usual if, but the same idea. The if module accepts a condition, a module, and a (possible empty) argument list. If the condition is true, the if module loads the module passed after the condition and calls its import() or unimport() method.
All that glued together: if $ARGV[0] is true, call the unimport() method of autovivification. This disables autovivification if $ARGV[0] is true, at compile time.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
|
|