Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.94k stars 554 forks source link

UNIVERSAL::AUTOLOAD breaks named captures/%-/%+ #11781

Closed p5pRT closed 12 years ago

p5pRT commented 12 years ago

Migrated from rt.perl.org#105024 (status was 'resolved')

Searchable as RT105024$

p5pRT commented 12 years ago

From @mauke

Created by @mauke

% perl -wle '{ package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"'
IO​::File​::DESTROY(IO​::File=IO(0x9447790)) at -e line 1. Use of uninitialized value $+{"X"} in string at -e line 1.

% perl -wle '%- if 0; { package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' IO​::File​::DESTROY(IO​::File=IO(0x823f798)) at -e line 1. a Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x823fba8)) at -e line 1 during global destruction. Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x823fca8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f078)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f0f8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f178)) at -e line 1 during global destruction.

So apparently just creating UNIVERSAL​::AUTOLOAD is enough to break %+\, even if AUTOLOAD is never called. But (for some reason) letting the parser see %+ early makes things work as expected.

As far as I can tell this is broken in 5.12 and 5.10 but works in 5.14\, probably because Tie​::Hash​::NamedCapture is a real (XS) module there.

Any idea what's going on?

Perl Info ``` Flags: category=core severity=low Site configuration information for perl 5.12.3: Configured by mauke at Tue Aug 9 13:00:53 CEST 2011. Summary of my perl5 (revision 5 version 12 subversion 3) configuration: Platform: osname=linux, osvers=2.6.37-gentoo-r4, archname=i686-linux uname='linux nora 2.6.37-gentoo-r4 #5 preempt sun may 8 03:58:55 cest 2011 i686 amd athlon(tm) 64 processor 3200+ authenticamd gnulinux ' config_args='-de -Dprefix=/home/mauke/usr/perlbrew/perls/perl-5.12.3' hint=recommended, useposix=true, d_sigaction=define useithreads=undef, usemultiplicity=undef useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef use64bitint=undef, use64bitall=undef, uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='cc', ccflags ='-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64', optimize='-O2', cppflags='-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include' ccversion='', gccversion='4.4.5', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, prototype=define Linker and Libraries: ld='cc', ldflags =' -fstack-protector -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat perllibs=-lnsl -ldl -lm -lcrypt -lutil -lc libc=/lib/libc-2.12.2.so, so=so, useshrplib=false, libperl=libperl.a gnulibc_version='2.12.2' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E' cccdlflags='-fPIC', lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector' Locally applied patches: @INC for perl 5.12.3: /home/mauke/usr/perlbrew/perls/perl-5.12.3/lib/site_perl/5.12.3/i686-linux /home/mauke/usr/perlbrew/perls/perl-5.12.3/lib/site_perl/5.12.3 /home/mauke/usr/perlbrew/perls/perl-5.12.3/lib/5.12.3/i686-linux /home/mauke/usr/perlbrew/perls/perl-5.12.3/lib/5.12.3 . Environment for perl 5.12.3: HOME=/home/mauke LANG=en_US.UTF-8 LANGUAGE (unset) LC_COLLATE=POSIX LD_LIBRARY_PATH=/home/mauke/usr/local/lib LOGDIR (unset) PATH=/home/mauke/usr/perlbrew/bin:/home/mauke/usr/perlbrew/perls/perl-5.12.3/bin:/home/mauke/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/opt/bin:/usr/i686-pc-linux-gnu/gcc-bin/4.4.5:/opt/sun-jdk-1.4.2.13/bin:/opt/sun-jdk-1.4.2.13/jre/bin:/opt/sun-jdk-1.4.2.13/jre/javaws:/opt/dmd/bin:/usr/games/bin PERLBREW_HOME=/home/mauke/.perlbrew PERLBREW_PATH=/home/mauke/usr/perlbrew/bin:/home/mauke/usr/perlbrew/perls/perl-5.12.3/bin PERLBREW_PERL=perl-5.12.3 PERLBREW_ROOT=/home/mauke/usr/perlbrew PERLBREW_VERSION=0.27 PERL_BADLANG (unset) PERL_UNICODE=SAL SHELL=/bin/bash ```
p5pRT commented 12 years ago

From @jkeenan

Additional data points ... This is what I got on Linux for first 5.12.0\, then 5.14.2​:

### $ /usr/local/bin/perl5.12.0 -wle '{ package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' IO​::File​::DESTROY(IO​::File=IO(0x817adf8)) at -e line 1. Use of uninitialized value $+{"X"} in string at -e line 1.

### $ perl -wle '{ package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' version​::DESTROY(v5.14.0) at -e line 1. version​::DESTROY(v5.14.0) at -e line 1. version​::DESTROY(v5.14.0) at -e line 1. version​::DESTROY(0.08) at -e line 1. version​::DESTROY(0.08) at -e line 1. version​::DESTROY(v5.14.0) at -e line 1. IO​::File​::DESTROY(IO​::File=IO(0x8189ff0)) at -e line 1. a Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x8196b68)) at -e line 1 during global destruction. Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x8196bd8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x8189e10)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x817ada8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x817ade8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x817ae28)) at -e line 1 during global destruction. ###

Similar results with these two Perl versions on Darwin.

p5pRT commented 12 years ago

The RT System itself - Status changed from 'new' to 'open'

p5pRT commented 12 years ago

From @cpansprout

On Wed Nov 30 13​:39​:47 2011\, l.mai@​web.de wrote​:

% perl -wle '{ package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' IO​::File​::DESTROY(IO​::File=IO(0x9447790)) at -e line 1. Use of uninitialized value $+{"X"} in string at -e line 1.

% perl -wle '%- if 0; { package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' IO​::File​::DESTROY(IO​::File=IO(0x823f798)) at -e line 1. a

Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x823fba8)) at -e line 1 during global destruction.

Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x823fca8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f078)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f0f8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f178)) at -e line 1 during global destruction.

So apparently just creating UNIVERSAL​::AUTOLOAD is enough to break %+\, even if AUTOLOAD is never called. But (for some reason) letting the parser see %+ early makes things work as expected.

As far as I can tell this is broken in 5.12 and 5.10 but works in 5.14\, probably because Tie​::Hash​::NamedCapture is a real (XS) module there.

Any idea what's going on?

It was indeed this commit that made the difference\, but I cannot understand why​:

commit 8dcfe2e99a72fe7951b4d15325e1541232823204 Author​: Nicholas Clark \nick@​ccl4\.org Date​: Thu Oct 14 15​:34​:03 2010 +0100

  Move remaining Tie​::Hash​::NamedCapture XS code to NamedCapture.xs  
  Now all the support code for %+ and %- is contained in the module in ext/

--

Father Chrysostomos

p5pRT commented 12 years ago

From @cpansprout

On Wed Nov 30 17​:49​:48 2011\, sprout wrote​:

On Wed Nov 30 13​:39​:47 2011\, l.mai@​web.de wrote​:

% perl -wle '{ package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' IO​::File​::DESTROY(IO​::File=IO(0x9447790)) at -e line 1. Use of uninitialized value $+{"X"} in string at -e line 1.

% perl -wle '%- if 0; { package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' IO​::File​::DESTROY(IO​::File=IO(0x823f798)) at -e line 1. a

Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x823fba8))

at -e line 1 during global destruction.

Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x823fca8))

at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f078)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f0f8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f178)) at -e line 1 during global destruction.

So apparently just creating UNIVERSAL​::AUTOLOAD is enough to break %+\, even if AUTOLOAD is never called. But (for some reason) letting the parser see %+ early makes things work as expected.

As far as I can tell this is broken in 5.12 and 5.10 but works in 5.14\, probably because Tie​::Hash​::NamedCapture is a real (XS) module there.

Any idea what's going on?

It was indeed this commit that made the difference\, but I cannot understand why​:

commit 8dcfe2e99a72fe7951b4d15325e1541232823204 Author​: Nicholas Clark \nick@​ccl4\.org Date​: Thu Oct 14 15​:34​:03 2010 +0100

Move remaining Tie​::Hash​::NamedCapture XS code to NamedCapture\.xs

Now all the support code for %\+ and %\- is contained in the module in

ext/

I’ve just figured it out. In gv.c​:

S_require_tie_mod(pTHX_ GV *gv\, const char *varpv\, SV* namesv\, const char *methpv\,const U32 flags) {   dVAR;   HV* stash = gv_stashsv(namesv\, 0);

  PERL_ARGS_ASSERT_REQUIRE_TIE_MOD;

  if (!stash || !(gv_fetchmethod(stash\, methpv))) {

In gv.h​:

#define gv_fetchmethod(stash\, name) gv_fetchmethod_autoload(stash\, name\, TRUE)

So\, if either the stash does not exist (as in 5.14; in 5.12 parts of it are in core)\, or the method (TIEHASH in this case) cannot be found\, even by autoloading\, then the module is loaded.

That means the problem still persists in 5.14. You just have to mention the package (e.g.\, ‘Tie​::Hash​::NamedCapture​::Googoo() if 0’) and the presence of a universal autoload sub will stop (compilation of) %- and %+ from loading the module.

S_require_tie_mod probably should not be autoloading.

Errno and arybase have the same problem​:

$ ./perl -Ilib -le 'arybase​::Googoo() if 0; { package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } $[ = 1; print qw(a b c)[2]' IO​::File​::DESTROY(IO​::File=IO(0x823220)) at -e line 1. c

(should print b)

$ ./perl -Ilib -le 'Errno​::onrrE() if 0; { package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } $!=20; print $!{ENOTDIR}' IO​::File​::DESTROY(IO​::File=IO(0x823220)) at -e line 1.

(should print 20\, on Unix at least)

--

Father Chrysostomos

p5pRT commented 12 years ago

From @cpansprout

On Wed Nov 30 18​:04​:50 2011\, sprout wrote​:

On Wed Nov 30 17​:49​:48 2011\, sprout wrote​:

On Wed Nov 30 13​:39​:47 2011\, l.mai@​web.de wrote​:

% perl -wle '{ package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' IO​::File​::DESTROY(IO​::File=IO(0x9447790)) at -e line 1. Use of uninitialized value $+{"X"} in string at -e line 1.

% perl -wle '%- if 0; { package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } "a" =~ /(?\.)/ or die; print "$+{X}"' IO​::File​::DESTROY(IO​::File=IO(0x823f798)) at -e line 1. a

Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x823fba8))

at -e line 1 during global destruction.

Tie​::Hash​::NamedCapture​::DESTROY(Tie​::Hash​::NamedCapture=SCALAR(0x823fca8))

at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f078)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f0f8)) at -e line 1 during global destruction. IO​::File​::DESTROY(IO​::File=IO(0x823f178)) at -e line 1 during global destruction.

So apparently just creating UNIVERSAL​::AUTOLOAD is enough to break %+\, even if AUTOLOAD is never called. But (for some reason) letting the parser see %+ early makes things work as expected.

As far as I can tell this is broken in 5.12 and 5.10 but works in 5.14\, probably because Tie​::Hash​::NamedCapture is a real (XS) module there.

Any idea what's going on?

It was indeed this commit that made the difference\, but I cannot understand why​:

commit 8dcfe2e99a72fe7951b4d15325e1541232823204 Author​: Nicholas Clark \nick@​ccl4\.org Date​: Thu Oct 14 15​:34​:03 2010 +0100

Move remaining Tie​::Hash​::NamedCapture XS code to NamedCapture\.xs

Now all the support code for %\+ and %\- is contained in the module in

ext/

I’ve just figured it out. In gv.c​:

S_require_tie_mod(pTHX_ GV *gv\, const char *varpv\, SV* namesv\, const char *methpv\,const U32 flags) { dVAR; HV* stash = gv_stashsv(namesv\, 0);

PERL\_ARGS\_ASSERT\_REQUIRE\_TIE\_MOD;

if \(\!stash || \!\(gv\_fetchmethod\(stash\, methpv\)\)\) \{

In gv.h​:

#define gv_fetchmethod(stash\, name) gv_fetchmethod_autoload(stash\, name\, TRUE)

So\, if either the stash does not exist (as in 5.14; in 5.12 parts of it are in core)\, or the method (TIEHASH in this case) cannot be found\, even by autoloading\, then the module is loaded.

That means the problem still persists in 5.14. You just have to mention the package (e.g.\, ‘Tie​::Hash​::NamedCapture​::Googoo() if 0’) and the presence of a universal autoload sub will stop (compilation of) %- and %+ from loading the module.

S_require_tie_mod probably should not be autoloading.

Errno and arybase have the same problem​:

$ ./perl -Ilib -le 'arybase​::Googoo() if 0; { package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } $[ = 1; print qw(a b c)[2]' IO​::File​::DESTROY(IO​::File=IO(0x823220)) at -e line 1. c

(should print b)

$ ./perl -Ilib -le 'Errno​::onrrE() if 0; { package UNIVERSAL; sub AUTOLOAD { warn "$AUTOLOAD(@​_)" } } $!=20; print $!{ENOTDIR}' IO​::File​::DESTROY(IO​::File=IO(0x823220)) at -e line 1.

(should print 20\, on Unix at least)

Now fixed with commit 0ea03996e. I hope my responses have given you enough information to pick the workaround of your choice.

Now\, back to tending the COWs....

--

Father Chrysostomos

p5pRT commented 12 years ago

@cpansprout - Status changed from 'open' to 'resolved'