#!/usr/bin/perl -T package Syntax::Tr::Ref; use v5.26; use warnings; use overload; use Carp; use PadWalker qw(peek_my peek_our); use experimental 'signatures'; sub ovl_tr ($,$str,$context) { # pass everything unmodified that is not an operand of tr/// return $str unless $context eq 'tr'; # ## get symbol table entry name #my ($name) = $str =~ /^\$(.*)/; ## pass regular operands #return $str unless defined $name; ## dereference the name #my $caller = caller(1); #no strict 'refs'; #my $ret = ${"${caller}::$name"}; # # # pass regular operands return $str unless $str =~ /^\$/; # search for a variable named $str defined as "my" or "our" in the # caller my $my = peek_my(1); my $our = peek_our(1); my $ret = exists $my->{$str} ? $my->{$str}->$* : exists $our->{$str} ? $our->{$str}->$* : undef; # # illegal argument croak qq(symbolic reference "\$${caller}::$name" not found) unless defined $ret; $ret; } sub import { overload::constant q => \&ovl_tr; } sub unimport { overload::remove_constant q => \&ovl_tr; } 1; package Foo; use v5.26; use warnings; use Carp::Always; # emulate "use Syntax::Tr::Ref;" BEGIN { Syntax::Tr::Ref->import; } # create a tainted empty string my $tainted = substr $ENV{PATH}, 0, 0; # a "bad" searchlist: tainted and contains the tr///-delimiter # ## must be an alias to a package variable #local our $abc = '%/cde' . $tainted; # # my $abc = '%/cde' . $tainted; # # the tr/// subject my $s = '%abcde/'; # eval on fixed string at runtime # almost equivalent to tr{%/cde}{@|fgh} eval 'tr/$abc/@|fgh/; 1' or warn $@ for $s; say $s; __DATA__ @abfgh|