#!./perl
BEGIN {
chdir
't'
if
-d
't'
;
require
'./test.pl'
;
set_up_inc(
'../lib'
);
}
plan(
tests
=> 169);
eval
'pass();'
;
is($@,
''
);
eval
"\$foo\n = # this is a comment\n'ok 3';"
;
is(
$foo
,
'ok 3'
);
eval
"\$foo\n = # this is a comment\n'ok 4\n';"
;
is(
$foo
,
"ok 4\n"
);
print
eval
'
$foo
=;';
like($@,
qr/line 2/
);
print
eval
'$foo = /'
;
like($@,
qr/Search/
);
is
scalar
(
eval
'++'
),
undef
,
'eval syntax error in scalar context'
;
is
scalar
(
eval
'die'
),
undef
,
'eval run-time error in scalar context'
;
is +()=
eval
'++'
, 0,
'eval syntax error in list context'
;
is +()=
eval
'die'
, 0,
'eval run-time error in list context'
;
is(
eval
'"ok 7\n";'
,
"ok 7\n"
);
$foo
= 5;
$fact
=
'if ($foo <= 1) {1;} else {push(@x,$foo--); (eval $fact) * pop(@x);}'
;
$ans
=
eval
$fact
;
is(
$ans
, 120,
'calculate a factorial with recursive evals'
);
$foo
= 5;
$fact
=
'local($foo)=$foo; $foo <= 1 ? 1 : $foo-- * (eval $fact);'
;
$ans
=
eval
$fact
;
is(
$ans
, 120,
'calculate a factorial with recursive evals'
);
my
$curr_test
= curr_test();
my
$tempfile
= tempfile();
open
(
try
,
'>'
,
$tempfile
);
print
try
'print "ok $curr_test\n";'
,
"\n"
;
close
try
;
do
"./$tempfile"
;
print
$@;
$i
=
$curr_test
+ 1;
for
(1..3) {
eval
'print "ok ", $i++, "\n"'
;
}
$curr_test
+= 4;
eval
{
print
"ok $curr_test\n"
;
die
sprintf
"ok %d\n"
,
$curr_test
+ 2;
1;
} ||
printf
"ok %d\n$@"
,
$curr_test
+ 1;
curr_test(
$curr_test
+ 3);
{
my
@a
=
qw(a b c d)
;
my
@b
=
eval
@a
;
is(
"@b"
,
'4'
);
is($@,
''
);
my
$a
=
q[defined(wantarray) ? (wantarray ? ($b='A') : ($b='S')) : ($b='V')]
;
my
$b
;
@a
=
eval
$a
;
is(
"@a"
,
'A'
);
is(
$b
,
'A'
);
$_
=
eval
$a
;
is(
$b
,
'S'
);
eval
$a
;
is(
$b
,
'V'
);
$b
=
'wrong'
;
$x
=
sub
{
my
$b
=
"right"
;
is(
eval
(
'"$b"'
),
$b
);
};
&$x
();
}
{
my
$b
=
'wrong'
;
my
$X
=
sub
{
my
$b
=
"right"
;
is(
eval
(
'"$b"'
),
$b
);
};
&$X
();
}
my
$x
=
'aa'
;
eval
<<'EOT'; die if $@;
print "# $x\n"; # clone into eval's pad
sub do_eval1 {
eval $_[0]; die if $@;
}
EOT
do_eval1(
'is($x, "aa")'
);
$x
++;
do_eval1(
'eval q[is($x, "ab")]'
);
$x
++;
do_eval1(
'sub { print "# $x\n"; eval q[is($x, "ac")] }->()'
);
$x
++;
eval
<<'EOT'; die if $@;
sub do_eval2 {
eval $_[0]; die if $@;
}
do_eval2('is($x, "ad")');
$x++;
do_eval2('eval q[is($x, "ae")]');
$x++;
do_eval2('sub { print "# $x\n"; eval q[is($x, "af")] }->()');
EOT
$main::ok
=
'not ok'
;
my
$ok
=
'ok'
;
eval
<<'EOT'; die if $@;
# $x unbound here
sub do_eval3 {
eval $_[0]; die if $@;
}
EOT
{
my
$ok
=
'not ok'
;
do_eval3(
'is($ok, q{ok})'
);
do_eval3(
'eval q[is($ok, q{ok})]'
);
do_eval3(
'sub { eval q[is($ok, q{ok})] }->()'
);
}
{
my
$x
= curr_test();
my
$got
;
sub
recurse {
my
$l
=
shift
;
if
(
$l
<
$x
) {
++
$l
;
eval
'print "# level $l\n"; recurse($l);'
;
die
if
$@;
}
else
{
$got
=
"ok $l"
;
}
}
local
$SIG
{__WARN__} =
sub
{ fail()
if
$_
[0] =~ /^Deep recurs/ };
recurse(curr_test() - 5);
is(
$got
,
"ok $x"
,
"recursive subroutine-call inside eval'' see its own lexicals"
);
}
eval
<<'EOT';
sub create_closure {
my $self = shift;
return sub {
return $self;
};
}
EOT
is(create_closure(
"good"
)->(),
"good"
,
'closures created within eval bind correctly'
);
$main::r
=
"good"
;
sub
terminal {
eval
'$r . q{!}'
}
is(
do
{
my
$r
=
"bad"
;
eval
'terminal($r)'
;
},
'good!'
,
'lexical search terminates correctly at subroutine boundary'
);
{
local
$SIG
{__DIE__} =
sub
{
eval
{1};
die
shift
};
eval
{
die
"wham_eth\n"
};
is($@,
"wham_eth\n"
);
}
{
my
$c
=
eval
"(1,2)x10"
;
is(
$c
,
'2222222222'
,
'scalar eval"" pops stack correctly'
);
}
{
my
$status
=
eval
{
eval
{
die
};
print
"# eval { return } test\n"
;
return
;
};
is($@,
''
,
'return from eval {} should clear $@ correctly'
);
}
{
my
$status
=
eval
q{
eval q{ die }
;
print
"# eval q{ return } test\n"
;
return
;
};
is($@,
''
,
'return from eval "" should clear $@ correctly'
);
}
{
eval
{
eval
{
goto
foo; };
like($@,
qr/Can't "goto" into the middle of a foreach loop/
,
'eval catches bad goto calls'
);
last
;
foreach
my
$i
(1) {
foo: fail(
'jumped into foreach'
);
}
};
fail(
"Outer eval didn't execute the last"
);
diag($@);
}
{
foreach
(
qw($$x @$x %$x $$$x)
) {
eval
'my '
.
$_
;
isnt($@,
''
,
"my $_ is forbidden"
);
}
}
{
$@ = 5;
eval
q{}
;
cmp_ok(
length
$@,
'=='
, 0,
'[ID 20020623.002 (#9721)] eval "" doesn\'t clear $@'
);
}
$::zzz = $::zzz = 0;
my
$zzz
= 1;
eval
q{
sub fred1 {
eval q{ is(eval '$zzz', 1); }
}
fred1(47);
{
my
$zzz
= 2; fred1(48) }
};
eval
q{
sub fred2 {
is(eval('$zzz'), 1);
}
};
fred2(49);
{
my
$zzz
= 2; fred2(50) }
sub
do_sort {
my
$zzz
= 2;
my
@a
=
sort
{ is(
eval
(
'$zzz'
), 2);
$a
<=>
$b
}
2, 1;
}
do_sort();
eval
q{
my $r = -1;
my $yyy = 9;
sub fred3 {
my $l = shift;
my $r = -2;
return 1 if $l < 1;
return 0 if eval '$zzz' != 1;
return 0 if $yyy != 9;
return 0 if eval '$yyy' != 9;
return 0 if eval '$l' != $l;
return $l * fred3($l-1);
}
my
$r
= fred3(5);
is(
$r
, 120);
$r
=
eval
'fred3(5)'
;
is(
$r
, 120);
$r
= 0;
eval
'$r = fred3(5)'
;
is(
$r
, 120);
$r
= 0;
{
my
$yyy
= 4;
my
$zzz
= 5;
my
$l
= 6;
$r
=
eval
'fred3(5)'
};
is(
$r
, 120);
};
my
$r
= fred3(5);
is(
$r
, 120);
$r
=
eval
'fred3(5)'
;
is(
$r
, 120);
$r
= 0;
eval
'$r = fred3(5)'
;
is(
$r
, 120);
$r
= 0;
{
my
$yyy
= 4;
my
$zzz
= 5;
my
$l
= 6;
$r
=
eval
'fred3(5)'
};
is(
$r
, 120);
my
$yyy
= 2;
sub
fred4 {
my
$zzz
= 3;
is(
$zzz
, 3);
is(
eval
'$zzz'
, 3);
is(
eval
'$yyy'
, 2);
}
eval
q{
fred4();
sub fred5 {
my $zzz = 4;
is($zzz, 4);
is(eval '$zzz', 4);
is(eval '$yyy', 2);
goto &fred4;
}
fred5();
};
fred5();
{
my
$yyy
= 88;
my
$zzz
= 99; fred5(); }
eval
q{ my $yyy = 888; my $zzz = 999; fred5(); }
;
{
$eval
=
eval
'sub { eval "sub { %S }" }'
;
$eval
->({});
pass(
'[perl #9728] used to dump core'
);
}
our
$x
= 1;
{
my
$x
=2;
sub
db1 {
$x
;
eval
'$x'
}
sub
DB::db2 {
$x
;
eval
'$x'
}
sub
db3 {
eval
'$x'
}
sub
DB::db4 {
eval
'$x'
}
sub
db5 {
my
$x
=4;
eval
'$x'
}
sub
db6 {
my
$x
=4;
eval
'$x'
}
}
{
my
$x
= 3;
is(db1(), 2);
is(DB::db2(), 2);
is(DB::db3(), 3);
is(DB::db4(), 3);
is(DB::db5(), 3);
is(db6(), 4);
my
sub
d6 {
DB::db3();
}
is(d6(), 3);
my
$y
;
my
$d7
=
sub
{
$y
;
DB::db3();
};
is(
$d7
->(), 3);
}
my
$got
= runperl (
prog
=>
'$h{a}=1; foreach my $k (keys %h) {eval qq{\$k}}'
,
stderr
=> 1);
is (
$got
,
''
);
{
my
%h
;
$h
{a}=1;
foreach
my
$k
(
keys
%h
) {
is(
$k
,
'a'
);
eval
"\$k"
;
is(
$k
,
'a'
);
}
}
sub
Foo {}
print
Foo(
eval
{});
pass(
'#20798 (used to dump core)'
);
{
my
(
@r
,
$r
,
$c
);
sub
context {
defined
(
wantarray
) ? (
wantarray
? (
$c
=
'A'
) : (
$c
=
'S'
)) : (
$c
=
'V'
) }
my
$code
=
q{ context() }
;
@r
=
qw( a b )
;
$r
=
'ab'
;
@r
=
eval
$code
;
is(
"@r$c"
,
'AA'
,
'string eval list context'
);
$r
=
eval
$code
;
is(
"$r$c"
,
'SS'
,
'string eval scalar context'
);
eval
$code
;
is(
"$c"
,
'V'
,
'string eval void context'
);
}
$got
= runperl (
prog
=>
'sub A::TIEARRAY { L: { eval { last L } } } tie @a, A; warn qq(ok\n)'
,
stderr
=> 1);
is(
$got
,
"ok\n"
,
'eval and last'
);
{
local
$@ =
"foo"
;
eval
undef
;
is($@,
""
,
'eval undef'
);
}
{
no
warnings;
eval
"&& $b;"
;
like($@,
qr/^syntax error/
,
'eval syntax error, no warnings'
);
}
{
my
$ok
= 0;
sub
STORE {
eval
'('
;
$ok
= 1 }
sub
TIESCALAR {
bless
[] }
my
$x
;
tie
$x
,
bless
[];
$x
= 1;
::is(
$ok
, 1,
'eval docatch'
);
}
$@ =
""
;
eval
{
die
"\x{a10d}"
; };
$_
=
length
$@;
eval
{ 1 };
cmp_ok($@,
'eq'
,
""
,
'length of $@ after eval'
);
cmp_ok(
length
$@,
'=='
, 0,
'length of $@ after eval'
);
SKIP: {
skip_if_miniperl(
'no dynamic loading on miniperl, no Devel::Peek'
, 2);
skip(
'Devel::Peek was not built'
, 2)
unless
$Config::Config
{extensions} =~ /\bDevel\/Peek\b/;
my
$tempfile
= tempfile();
open
$prog
,
">"
,
$tempfile
or
die
"Can't create test file"
;
print
$prog
<<'END_EVAL_TEST';
use Devel::Peek;
$! = 0;
$@ = $!;
Dump($@);
print STDERR "******\n";
eval { die "\x{a10d}"; };
$_ = length $@;
eval { 1 };
Dump($@);
print STDERR "******\n";
print STDERR "Done\n";
END_EVAL_TEST
close
$prog
or
die
"Can't close $tempfile: $!"
;
my
$got
= runperl(
progfile
=>
$tempfile
,
stderr
=> 1);
my
(
$first
,
$second
,
$tombstone
) =
split
(/\*\*\*\*\*\*\n/,
$got
);
is(
$tombstone
,
"Done\n"
,
'Program completed successfully'
);
$first
=~ s/p?[NI]OK,//g;
s/ PV = 0x[0-9a-f]+/ PV = 0x/
foreach
$first
,
$second
;
s/ LEN = [0-9]+/ LEN = /
foreach
$first
,
$second
;
$second
=~ s/ IV = 0\n\n/ IV = 0\n/
if
$^O eq
'VMS'
;
is(
$second
,
$first
,
'eval { 1 } completely resets $@'
);
}
{
my
$count_expected
= ($^H & 0x20000) ? 2 : 1;
my
$t
;
my
$s
=
"a"
;
$s
=~ s/a/
$t
= \%^H;
qq( qq()
);/ee;
refcount_is
$t
,
$count_expected
,
'RT 63110'
;
}
{
local
$_
=
"21+12"
;
is(
eval
, 33,
'argless eval without hints'
);
local
$_
=
"42+24"
;
is(
eval
, 66,
'argless eval with hints'
);
}
{
my
$x
;
refcount_is \
$x
, 1+1,
"originally only 1 reference"
;
eval
'$x'
;
refcount_is \
$x
, 1+1,
"execution eval doesn't create new references"
;
}
fresh_perl_is(
<<'EOP', "ok\n", undef, 'RT #70862');
$::{'@'}='';
eval {};
print "ok\n";
EOP
fresh_perl_is(
<<'EOP', "ok\n", undef, 'variant of RT #70862');
eval {
$::{'@'}='';
};
print "ok\n";
EOP
fresh_perl_is(
<<'EOP', "ok\n", undef, 'related to RT #70862');
$::{'@'}=\3;
eval {};
print "ok\n";
EOP
fresh_perl_is(
<<'EOP', "ok\n", undef, 'related to RT #70862');
eval {
$::{'@'}=\3;
};
print "ok\n";
EOP
fresh_perl_is(
<<'EOP', "ok\n", undef, 'segfault on syntax errors in block evals');
# localize the hits hash so the eval ends up with the pad offset of a copy of it in its targ
BEGIN { $^H |= 0x00020000 }
eval q{ eval { + } };
print "ok\n";
EOP
fresh_perl_is(
<<'EOP', "ok\n", undef, 'assert fail on non-string in Perl_lex_start');
use overload '""' => sub { '1;' };
my $ov = bless [];
eval $ov;
print "ok\n";
EOP
for
my
$k
(!0) {
eval
'my $do_something_with = $k'
;
eval
{
$k
=
'mon'
};
is
"a"
=~ /a/,
"1"
,
"string eval leaves readonly lexicals readonly [perl #19135]"
;
}
fresh_perl_is(
<<'EOP', "ok\nok\nok\n", undef, 'eval clears %^H');
BEGIN {
require re; re->import('/x'); # should only affect surrounding scope
eval '
print "a b" =~ /a b/ ? "ok\n" : "nokay\n";
use re "/m";
print "a b" =~ /a b/ ? "ok\n" : "nokay\n";
';
}
print "ab" =~ /a b/ ? "ok\n" : "nokay\n";
EOP
{
BEGIN {
eval
'require re; import re "/x"'
}
ok
"ab"
=~ /a b/,
'eval does not localise %^H at run time'
;
}
eval
(
q|""!=!~//|
);
pass(
"phew! dodged the assertion after a parsing (not lexing) error"
);
{
local
$ENV
{PERL_DESTRUCT_LEVEL} = 1;
unlike
runperl(
prog
=>
'BEGIN { $^H{foo} = bar }'
.
'our %FIELDS; my main $x; eval q[$x->{foo}]'
,
stderr
=> 1,
),
qr/Unbalanced string table/
,
'Errors in finalize_optree do not leak string eval op tree'
;
}
for
(
"{;"
,
"{"
) {
eval
$_
; is $@ =~ s/
eval
\d+/
eval
1/rag,
<<'EOE',
Missing right curly or square bracket at (eval 1) line 1, at end of line
syntax error at (eval 1) line 1, at EOF
Execution of (eval 1) aborted due to compilation errors.
EOE
qq'Right line number for eval "$_"'
;
}
{
my
$w
;
local
$SIG
{__WARN__} =
sub
{
$w
.=
shift
};
eval
"\${\nfoobar\n} = 10; warn q{should be line 3}"
;
is(
$w
=~ s/
eval
\d+/
eval
1/ra,
"should be line 3 at (eval 1) line 3.\n"
,
'eval qq{\${\nfoo\n}; warn} updates the line number correctly'
);
}
sub
_117941 {
package
_117941;
eval
'$a'
}
delete
$::{
"_117941::"
};
_117941();
pass(
"eval in freed package does not crash"
);
{
$@ = 1;
eval
q{$@ = 2}
;
ok(!$@,
'eval clearing $@'
);
}
{
sub
f127786 {
eval
q/\$s/
}
}
my
$s
;
sub
{
$s
; DB::f127786}->();
pass(
"RT #127786"
);
}
{
sub
DESTROY {
eval
{
die
"died in DESTROY"
; } }
eval
{ { 1; { 1;
bless
[]; } } };
::is ($@,
""
,
"FREETMPS: normal try exit"
);
eval
q{ { 1; { 1; bless []; }
} };
::is ($@,
""
,
"FREETMPS: normal string eval exit"
);
eval
{ { 1; { 1;
return
bless
[]; } } };
::is ($@,
""
,
"FREETMPS: return try exit"
);
eval
q{ { 1; { 1; return bless []; }
} };
::is ($@,
""
,
"FREETMPS: return string eval exit"
);
eval
{ { 1; { 1;
my
$x
=
bless
[];
die
$x
= 0,
"die in eval"
; } } };
::like ($@,
qr/die in eval/
,
"FREETMPS: die try exit"
);
eval
q{ { 1; { 1; my $x = bless []; die $x = 0, "die in eval"; }
} };
::like ($@,
qr/die in eval/
,
"FREETMPS: die eval string exit"
);
}
{
local
${^MAX_NESTED_EVAL_BEGIN_BLOCKS}= 0;
my
(
$x
,
$ok
);
$x
= 0;
$ok
=
eval
'BEGIN { $x++ } 1'
;
::ok(!
$ok
,
'${^MAX_NESTED_EVAL_BEGIN_BLOCKS} = 0 blocks BEGIN blocks entirely'
);
::like($@,
qr/Too many nested BEGIN blocks, maximum of 0 allowed/
,
'Blocked BEGIN results in expected error'
);
::is(
$x
,0,
'BEGIN really did nothing'
);
${^MAX_NESTED_EVAL_BEGIN_BLOCKS}= 2;
$ok
=
eval
'sub f { my $n= shift; eval q[BEGIN { $x++; f($n-1) if $n>0 } 1] or die $@ } f(3); 1'
;
::ok(!
$ok
,
'${^MAX_NESTED_EVAL_BEGIN_BLOCKS} = 2 blocked three nested BEGIN blocks'
);
::like($@,
qr/Too many nested BEGIN blocks, maximum of 2 allowed/
,
'Blocked BEGIN results in expected error'
);
::is(
$x
,2,
'BEGIN really did nothing'
);
}
{
foreach
my
$line
(
'eval "UNITCHECK { eval q(UNITCHECK { die; }); print q(A-) }";'
,
'eval "UNITCHECK { eval q(BEGIN { die; }); print q(A-) }";'
,
'eval "BEGIN { eval q(UNITCHECK { die; }); print q(A-) }";'
,
'CHECK { eval "]" } print q"A-";'
,
'INIT { eval "]" } print q"A-";'
,
'UNITCHECK { eval "]" } print q"A-";'
,
'BEGIN { eval "]" } print q"A-";'
,
'INIT { eval q(UNITCHECK { die; } print 0;); print q(A-); }'
,
) {
fresh_perl_is(
$line
.
' print "ok";'
,
"A-ok"
, {},
"No segfault: $line"
);
my
$sort_line
=
'my @x= sort { '
.
$line
.
' } 1,2;'
;
fresh_perl_is(
$sort_line
.
' print "ok";'
,
"A-ok"
, {},
"No segfault inside sort: $sort_line"
);
}
}
{
for
my
$fragment
(
'bar'
,
'1+;'
,
'1+;'
x 11,
's/'
,
']'
) {
fresh_perl_is(
'use strict; use warnings; $SIG{__DIE__} = sub { die "X" }; '
.
'eval { eval "'
.
$fragment
.
'"; print "after eval $@"; };'
.
'if ($@) { print "outer eval $@" }'
,
"after eval X at - line 1."
,
{},
"test that nested eval '$fragment' calls sig die as expected"
);
}
}