perl5 icon indicating copy to clipboard operation
perl5 copied to clipboard

Attribute::Handlers - cached symbol lookup fails when used with string eval to redefine subs

Open NorthboundTrain opened this issue 2 years ago • 1 comments

This is a bug report for perl generated with the help of perlbug 1.43 running under perl 5.38.0.

Module: Attribute::Handlers

Description

I was trying to write some unit tests for a module that makes use of code attributes via Attribute::Handlers. Because of the way that the attributes might interact with child functions I wanted to run through a set of three nested subs with each combination of with and without attributes (so eight tests total). I wrote it with three loops using string eval to redefine the functions each iteration.

The original code made use of the function name, derived via *{$symbol}{NAME} and what I noticed was that sometimes the function name did not match up with the sub that had the attribute defined on it. It failed quite consistently.

Steps to Reproduce

Here's the stripped down code with some added debug statements:

#!/usr/bin/env perl

use strict;
use warnings;

use 5.038;

use Attribute::Handlers;

sub TestAttr : ATTR(CODE)
{
    my ($package, $symbol, $referent, $attr, $data, $phase, $filename, $linenum) = @_;
    my $function_name = *{$symbol}{NAME};

say("FN: $function_name / $referent / $symbol / @$data / $linenum");

    return;
}

my($sub_def_str) = <<EOS;
    no warnings qw(redefine);

    sub foo :FooAttr { }
    sub bar :BarAttr { }
    sub baz :BazAttr { }
EOS


foreach my $foo (":TestAttr(xFOO)", "") {
    foreach my $bar (":TestAttr(xBAR)", "") {
	foreach my $baz (":TestAttr(xBAZ)", "") {
say("---------");
my($attrs) = []; for ([$foo, "foo"], [$bar, "bar"], [$baz, "baz"]) { push(@$attrs, $_->[1]) if $_->[0] };
say("attrs: " . join(", ", @$attrs));
	    my($newdef) = "$sub_def_str";
	    $newdef =~ s{ :FooAttr}{ $foo}g;
	    $newdef =~ s{ :BarAttr}{ $bar}g;
	    $newdef =~ s{ :BazAttr}{ $baz}g;

	    eval "$newdef";
	    die $@ if $@;
	}
    }
}

Expected behavior

The following output is produced:

---------
attrs: foo, bar, baz
FN: foo / CODE(0x7fc8eb02c5e8) / GLOB(0x7fc8ec0130f8) / xFOO / 3
FN: bar / CODE(0x7fc8eb02c5d0) / GLOB(0x7fc8eb03a200) / xBAR / 4
FN: baz / CODE(0x7fc8eb02c6f0) / GLOB(0x7fc8eb02c348) / xBAZ / 5
---------
attrs: foo, bar
FN: foo / CODE(0x7fc8eb02b690) / GLOB(0x7fc8ec0130f8) / xFOO / 3
FN: bar / CODE(0x7fc8ec083ef0) / GLOB(0x7fc8eb03a200) / xBAR / 4
---------
attrs: foo, baz
FN: foo / CODE(0x7fc8ec09fae0) / GLOB(0x7fc8ec0130f8) / xFOO / 3
FN: bar / CODE(0x7fc8ec0c77e0) / GLOB(0x7fc8eb03a200) / xBAZ / 5
---------
attrs: foo
FN: foo / CODE(0x7fc8eb03ab30) / GLOB(0x7fc8ec0130f8) / xFOO / 3
---------
attrs: bar, baz
FN: foo / CODE(0x7fc8eb025378) / GLOB(0x7fc8ec0130f8) / xBAR / 4
FN: bar / CODE(0x7fc8eb03ab30) / GLOB(0x7fc8eb03a200) / xBAZ / 5
---------
attrs: bar
FN: bar / CODE(0x7fc8eb025378) / GLOB(0x7fc8eb03a200) / xBAR / 4
---------
attrs: baz
FN: bar / CODE(0x7fc8ec013068) / GLOB(0x7fc8eb03a200) / xBAZ / 5
---------
attrs:

The first two iterations are fine but the third, fifth, and seventh iterations show the issue. In the third one, the attributes are defined on foo and baz (as shown by the attribute arguments xFoo and xBaz), but the baz function symbol turns up as bar. In the fifth, the attributes are defined on bar and baz, but the bar function symbol turns up as foo, and on the seventh the attribute is defined on baz, but the symbol turns up as bar.

My original StackOverflow posting has some more details: https://stackoverflow.com/q/77643341/45978

There seems to be a problem with the redefinition of the subs and the cached symbol lookup in Attribute::Handlers. From the S/O user clamp:

Attribute::Handlers uses a cache for symbol lookups in A::H::findsym(). There seems to be a problem with redefinitions of subs not invalidating the cache. If I comment out line 12 in Attribute::Handlers:

# return $symcache{$pkg,$ref} if $symcache{$pkg,$ref};

everything works as expected.

Whether the cache needs to be invalidated automagically at some point or the symbol reference is being re-used incorrectly by eval is beyond my depth of understanding. Or perhaps everything is working as reasonably can be expected and there needs to be a way to manually invalidate the cache if someone is redefining subs repeatedly (which should never happen in a real program, and it is arguable whether it should even happen in a test program, however it's valid code, so it should be able to work somehow, IMHO).

Perl configuration

Site configuration information for perl 5.38.0:

Configured by SomeUser at Wed Nov 29 22:45:53 EST 2023.

Summary of my perl5 (revision 5 version 38 subversion 0) configuration:

  Platform:
    osname=darwin
    osvers=21.6.0
    archname=darwin-2level
    uname='darwin compass.local 21.6.0 darwin kernel version 21.6.0: thu jul 6 22:18:26 pdt 2023; root:xnu-8020.240.18.702.13~1release_x86_64 x86_64 i386 darwin '
    config_args='-Dprefix=/opt/perl -des'
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=undef
    usemultiplicity=undef
    use64bitint=define
    use64bitall=define
    uselongdouble=undef
    usemymalloc=n
    default_inc_excludes_dot=define
  Compiler:
    cc='cc'
    ccflags ='-fno-common -DPERL_DARWIN -mmacosx-version-min=12.6 -DNO_POSIX_2008_LOCALE -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -I/opt/local/include'
    optimize='-O3'
    cppflags='-fno-common -DPERL_DARWIN -mmacosx-version-min=12.6 -DNO_POSIX_2008_LOCALE -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -I/opt/local/include'
    ccversion=''
    gccversion='Apple LLVM 14.0.0 (clang-1400.0.29.202)'
    gccosandvers=''
    intsize=4
    longsize=8
    ptrsize=8
    doublesize=8
    byteorder=12345678
    doublekind=3
    d_longlong=define
    longlongsize=8
    d_longdbl=define
    longdblsize=16
    longdblkind=3
    ivtype='long'
    ivsize=8
    nvtype='double'
    nvsize=8
    Off_t='off_t'
    lseeksize=8
    alignbytes=8
    prototype=define
  Linker and Libraries:
    ld='cc'
    ldflags =' -mmacosx-version-min=12.6 -fstack-protector-strong -L/usr/local/lib -L/opt/local/lib'
    libpth=/usr/local/lib /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/14.0.0/lib /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib /opt/local/lib /usr/lib
    libs=-lgdbm
    perllibs=
    libc=
    so=dylib
    useshrplib=false
    libperl=libperl.a
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_dlopen.xs
    dlext=bundle
    d_dlsymun=undef
    ccdlflags=' '
    cccdlflags=' '
    lddlflags=' -mmacosx-version-min=12.6 -bundle -undefined dynamic_lookup -L/usr/local/lib -L/opt/local/lib -fstack-protector-strong'


---
@INC for perl 5.38.0:
    /opt/perl/lib/site_perl/5.38.0/darwin-2level
    /opt/perl/lib/site_perl/5.38.0
    /opt/perl/lib/5.38.0/darwin-2level
    /opt/perl/lib/5.38.0

---
Environment for perl 5.38.0:
    DYLD_LIBRARY_PATH (unset)
    HOME=/Users/SomeUser
    LANG=en_US.UTF-8
    LANGUAGE (unset)
    LC_TERMINAL=iTerm2
    LC_TERMINAL_VERSION=3.4.22
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/opt/perl/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin
    PERL_BADLANG (unset)
    SHELL=/bin/bash

NorthboundTrain avatar Dec 13 '23 14:12 NorthboundTrain

Is there anyone familiar with Attribute::Handlers who could take a look at this ticket? thanks.

jkeenan avatar Feb 12 '24 01:02 jkeenan