Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eval '$foo' in package DB called from lexical sub can not see closure values or globals #19370

Closed
jimav opened this issue Jan 27, 2022 · 2 comments · Fixed by #21489
Closed

Comments

@jimav
Copy link

jimav commented Jan 27, 2022

Perl v5.30.0

eval "string" in package DB is supposed to be evaluated in the nearest non-DB context, useful for debug tools. However when code in package DB is called from a nested lexical ("my") sub, only the lexicals in the innermost sub are visible; globals and lexicals in enclosing scopes can no longer be accessed.

(This may seem obscure, but it is important to me because I use some debugging tools which depend on eval in package DB being able to see user-defined lexicals).

The output from the following script is:

$w_from_outer = 41;
$x_from_outer = 42;
$y_from_outer = 43;
$w_from_inner = undef;
$x_from_inner = undef;
$y_from_inner = undef;
$z_from_inner = 44;

and here is the script:

!/usr/bin/perl
use strict; use warnings; use v5.18; use Data::Dumper;

our $w = 41;
my $x = 42;

my ($w_from_outer, $x_from_outer, $y_from_outer, 
    $w_from_inner, $x_from_inner, $y_from_inner, $z_from_inner);

sub outer {
  my $y = 43;

  $w_from_outer = &DB::doeval('$w');
  $x_from_outer = &DB::doeval('$x');
  $y_from_outer = &DB::doeval('$y');

  my sub inner {
    my $z = 44;

    $w_from_inner = &DB::doeval('$w'); # gets undef
    $x_from_inner = &DB::doeval('$x'); # gets undef
    $y_from_inner = &DB::doeval('$y'); # gets undef
    $z_from_inner = &DB::doeval('$z');
  }
  inner();
}
outer();

say Data::Dumper->Dump(
      [
        $w_from_outer, $x_from_outer, $y_from_outer,
        $w_from_inner, $x_from_inner, $y_from_inner, $z_from_inner
      ],
      [qw(
        w_from_outer x_from_outer y_from_outer
        w_from_inner x_from_inner y_from_inner z_from_inner
      )] );
      
package DB;

sub doeval($) {
  return eval $_[0];
}
@tonycoz
Copy link
Contributor

tonycoz commented Jun 26, 2023

This looks like a consequence of a0d2bbd.

This broke the CvOUTSIDE() links from the inner sub to the outer sub, so the outer names weren't visible from the inner sub, unless the inner sub has an eval or the debugger is enabled.

The documentation for eval doesn't require that the debugger be enabled, so this is definitely a bug.

I haven't worked out a fix yet, given this works if the inner sub is anonymous, I suspect some CvANON(cv) needs to be CvANON(cv) || CvLEXICAL(cv).

@tonycoz
Copy link
Contributor

tonycoz commented Jun 27, 2023

Actually it happens for anonymous subs too, if that sub is a closure:

$ ./perl -Ilib   ../19370c.pl 
12
Global symbol "$x" requires explicit package name (did you forget to declare "my $x"?) at (eval 1) line 1.
$ cat ../19370c.pl
use v5.36.0;
sub x {
    my $x = 10;
    my $y = 12;
    my $j  = sub {
        my $z = 11;
        say $y;
        DB::do_eval('say $x, $z; 1;');
    };

    $j->();
}

x();

package DB;

sub do_eval {
    eval $_[0] or die $@;
}
$

Comments out the say $y; and the eval compiles.

So my speculation that including CvLEXICAL() subs was incorrect.

If I make setting CvOUTSIDE() for the clone unconditional, as it was before a0d2bbd, both the lexical inner sub and the anonymous closure work.

At this point I think the change from a0d2bbd is just broken, this was a fix for #11286, we need the CvOUTSIDE() links to implement this documented (in 2002) behaviour.

@Leont and @iabyn both commented on the original ticket.

@iabyn wrote the original change d819b83 that implemented this visibility.

tonycoz added a commit to tonycoz/perl5 that referenced this issue Sep 14, 2023
But they do need CvOUTSIDE() for eval-in-package-DB's scope
magic to work correctly.

This also incidentally fixes a TODO Deparse, since the CvOUTSIDE
is now available

Fixes Perl#19370 aka rt89544

This breaks Perl#11286, but I don't think that's fixable without breaking
eval-in-DB's special scope behaviour.

This reverts (most of) commit a0d2bbd.
tonycoz added a commit to tonycoz/perl5 that referenced this issue Sep 14, 2023
But they do need CvOUTSIDE() for eval-in-package-DB's scope
magic to work correctly.

This also incidentally fixes a TODO Deparse, since the CvOUTSIDE
is now available

Fixes Perl#19370 aka rt89544

This breaks Perl#11286, but I don't think that's fixable without breaking
eval-in-DB's special scope behaviour.

This reverts (most of) commit a0d2bbd.
tonycoz added a commit that referenced this issue Sep 25, 2023
But they do need CvOUTSIDE() for eval-in-package-DB's scope
magic to work correctly.

This also incidentally fixes a TODO Deparse, since the CvOUTSIDE
is now available

Fixes #19370 aka rt89544

This breaks #11286, but I don't think that's fixable without breaking
eval-in-DB's special scope behaviour.

This reverts (most of) commit a0d2bbd.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants