line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Eval::Safe; |
2
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
131800
|
use 5.022; |
|
3
|
|
|
|
|
21
|
|
4
|
3
|
|
|
3
|
|
12
|
use strict; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
50
|
|
5
|
3
|
|
|
3
|
|
10
|
use warnings; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
78
|
|
6
|
|
|
|
|
|
|
|
7
|
3
|
|
|
3
|
|
13
|
use Carp; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
210
|
|
8
|
3
|
|
|
3
|
|
1114
|
use Eval::Safe::Eval; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
289
|
|
9
|
3
|
|
|
3
|
|
1005
|
use Eval::Safe::Safe; |
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
113
|
|
10
|
3
|
|
|
3
|
|
18
|
use List::Util qw(none); |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
277
|
|
11
|
3
|
|
|
3
|
|
19
|
use Scalar::Util qw(reftype refaddr); |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
1196
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub new { |
16
|
4
|
|
|
4
|
1
|
143359
|
my ($class, %options) = @_; |
17
|
4
|
50
|
|
|
|
14
|
croak "Eval::Safe->new called with invalid class name: $class" unless $class eq 'Eval::Safe'; |
18
|
4
|
|
|
|
|
11
|
my @known_options = qw(safe strict warnings debug package force_package); |
19
|
4
|
|
|
8
|
|
9
|
my @unknown_options = grep {my $k = $_; none { $k eq $_ } @known_options } keys %options; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
18
|
|
20
|
4
|
100
|
|
|
|
11
|
if (@unknown_options) { |
21
|
1
|
|
|
|
|
165
|
croak "Unknown options: ".join(' ', @unknown_options); |
22
|
|
|
|
|
|
|
} |
23
|
3
|
|
|
|
|
12
|
$options{strict} = _make_pragma('strict', $options{strict}); |
24
|
3
|
|
|
|
|
10
|
$options{warnings} = _make_pragma('warnings', $options{warnings}); |
25
|
3
|
50
|
33
|
|
|
12
|
if ($options{package} and not $options{force_package}) { |
26
|
0
|
|
|
|
|
0
|
$options{package} = Eval::Safe::_validate_package_name($options{package}); |
27
|
0
|
0
|
|
|
|
0
|
croak "Package $options{package} already exists" if eval "%$options{package}::"; |
28
|
|
|
|
|
|
|
} |
29
|
3
|
100
|
100
|
|
|
58
|
if ($options{safe} // 0 > 0) { |
30
|
1
|
|
|
|
|
8
|
return Eval::Safe::Safe->new(%options); |
31
|
|
|
|
|
|
|
} else { |
32
|
2
|
|
|
|
|
11
|
return Eval::Safe::Eval->new(%options); |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub package { |
37
|
0
|
|
|
0
|
1
|
0
|
my ($this) = @_; |
38
|
0
|
|
|
|
|
0
|
return $this->{package}; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub wrap { |
42
|
0
|
|
|
0
|
1
|
0
|
my ($this, $code) = @_; |
43
|
0
|
|
|
|
|
0
|
return $this->eval("sub { ${code} }"); |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub share { |
47
|
0
|
|
|
0
|
1
|
0
|
my ($this, @vars) = @_; |
48
|
0
|
|
|
|
|
0
|
my $calling_package = caller; |
49
|
0
|
|
|
|
|
0
|
$this->share_from($calling_package, @vars); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub share_from { |
53
|
0
|
|
|
0
|
1
|
0
|
my ($this, $package, @vars) = @_; |
54
|
0
|
|
|
|
|
0
|
$package = _validate_package_name($package); |
55
|
0
|
0
|
|
|
|
0
|
croak "Package $package does not exist" unless eval "%${package}::"; |
56
|
0
|
|
|
|
|
0
|
for my $v (@vars) { |
57
|
0
|
0
|
|
|
|
0
|
croak "Variable has no leading sigil: $v" unless $v =~ m'^([&*$%@])(\w+)$'; |
58
|
0
|
|
|
|
|
0
|
my ($sigil, $symbol) = ($1, $2); |
59
|
|
|
|
|
|
|
# There are only 5 different sigils, so we could skip the eval here and |
60
|
|
|
|
|
|
|
# instead branch on the $sigil and use a syntax like the one on the left of |
61
|
|
|
|
|
|
|
# the equal (e.g. \&{$package."::$symbol"}). See: |
62
|
|
|
|
|
|
|
# https://metacpan.org/source/MICB/Safe-b2/Safe.pm |
63
|
3
|
|
|
3
|
|
21
|
no strict 'refs'; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
367
|
|
64
|
0
|
|
|
|
|
0
|
*{($this->package())."::${symbol}"} = eval "\\${sigil}${package}::${symbol}"; |
|
0
|
|
|
|
|
0
|
|
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub var_ref { |
69
|
0
|
|
|
0
|
1
|
0
|
my ($this, $var) = @_; |
70
|
0
|
0
|
|
|
|
0
|
croak "Variable has no leading sigil: $var" unless $var =~ m'^([&*$%@])(\w+)$'; |
71
|
|
|
|
|
|
|
# There are only 5 different sigils, so we could skip the eval here and |
72
|
|
|
|
|
|
|
# instead branch on the $sigil. See: |
73
|
|
|
|
|
|
|
# https://metacpan.org/source/MICB/Safe-b2/Safe.pm |
74
|
3
|
|
|
3
|
|
16
|
no strict 'refs'; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
1306
|
|
75
|
0
|
|
|
|
|
0
|
return eval sprintf '\%s%s::%s', $1, $this->package(), $2; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub interpolate { |
79
|
0
|
|
|
0
|
1
|
0
|
my ($this, $str) = @_; |
80
|
|
|
|
|
|
|
# It's not clear if Text::Balanced could help here. |
81
|
0
|
|
|
|
|
0
|
my $r = $this->eval("<<\"EVAL_SAFE_EOF_WORD\"\n${str}\nEVAL_SAFE_EOF_WORD\n"); |
82
|
0
|
|
|
|
|
0
|
$r =~ s/\n$//; |
83
|
0
|
|
|
|
|
0
|
return $r; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# _make_pragma('pragma', $arg) |
87
|
|
|
|
|
|
|
# Returns a string saying "no pragma" if $arg is false, "use pragma" if arg is |
88
|
|
|
|
|
|
|
# a `true` scalar, "use pragma $$arg" if arg is a scalar reference, and |
89
|
|
|
|
|
|
|
# "use pragma @$arg" if arg is an array reference. |
90
|
|
|
|
|
|
|
sub _make_pragma() { |
91
|
6
|
|
|
6
|
|
14
|
my ($pragma, $arg) = @_; |
92
|
6
|
|
|
|
|
13
|
my $reftype = reftype $arg; |
93
|
6
|
50
|
|
|
|
10
|
if (not defined $reftype) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
94
|
6
|
50
|
|
|
|
9
|
if ($arg) { |
95
|
0
|
|
|
|
|
0
|
return "use ${pragma};"; |
96
|
|
|
|
|
|
|
} else { |
97
|
6
|
|
|
|
|
16
|
return "no ${pragma};"; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
} elsif ($reftype eq 'SCALAR') { |
100
|
0
|
|
|
|
|
|
return "use ${pragma} '$arg';"; |
101
|
|
|
|
|
|
|
} elsif ($reftype eq 'ARRAY') { |
102
|
|
|
|
|
|
|
# We should probably use Data::Dumper to format the arg list properly in |
103
|
|
|
|
|
|
|
# case some of the args contain a space. |
104
|
0
|
|
|
|
|
|
return ("use ${pragma} qw(".join(' ', @$arg).');'); |
105
|
|
|
|
|
|
|
} elsif ($reftype eq 'HASH') { |
106
|
0
|
|
|
|
|
|
return ("use ${pragma} qw(".join(' ', %$arg).');'); |
107
|
|
|
|
|
|
|
} else { |
108
|
0
|
|
|
|
|
|
croak "Invalid argument for '${pragma}' option, expected a scalar or array reference"; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# $safe->_wrap_code_refs('sub', @objects) |
113
|
|
|
|
|
|
|
# will call $safe->sub($ref) for all code references found within @objects and |
114
|
|
|
|
|
|
|
# store the result in place in @objects. The passed objects are crawled |
115
|
|
|
|
|
|
|
# recursively. |
116
|
|
|
|
|
|
|
# Finally, the modified array is returned. |
117
|
|
|
|
|
|
|
# |
118
|
|
|
|
|
|
|
# This is similar to the wrap_code_refs_within method in Safe. |
119
|
|
|
|
|
|
|
sub _wrap_code_refs { |
120
|
0
|
|
|
0
|
|
|
my ($this, $wrapper) = splice @_, 0, 2; |
121
|
|
|
|
|
|
|
# We need to use @_ below (without giving it a new name) to retain its |
122
|
|
|
|
|
|
|
# aliasing property to modify the arguments in-place. |
123
|
0
|
|
|
|
|
|
my %seen_refs = (); |
124
|
|
|
|
|
|
|
my $crawler = sub { |
125
|
0
|
|
|
0
|
|
|
for my $item (@_) { |
126
|
0
|
|
|
|
|
|
my $reftype = reftype $item; |
127
|
0
|
0
|
|
|
|
|
next unless $reftype; |
128
|
0
|
0
|
|
|
|
|
next if ++$seen_refs{refaddr $item} > 1; |
129
|
0
|
0
|
|
|
|
|
if ($reftype eq 'ARRAY') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
130
|
0
|
|
|
|
|
|
__SUB__->(@$item); # __SUB__ is the current sub. |
131
|
|
|
|
|
|
|
} elsif ($reftype eq 'HASH') { |
132
|
0
|
|
|
|
|
|
__SUB__->(values %$item); |
133
|
|
|
|
|
|
|
} elsif ($reftype eq 'CODE') { |
134
|
0
|
|
|
|
|
|
$item = $this->$wrapper($item); |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
# We're ignoring the GLOBs for the time being. |
137
|
|
|
|
|
|
|
} |
138
|
0
|
|
|
|
|
|
}; |
139
|
0
|
|
|
|
|
|
$crawler->(@_); |
140
|
0
|
0
|
|
|
|
|
if (defined wantarray) { |
141
|
0
|
0
|
|
|
|
|
return (wantarray) ? @_ : $_[0]; |
142
|
|
|
|
|
|
|
} |
143
|
0
|
|
|
|
|
|
return; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# _validate_package_name('package::name') |
147
|
|
|
|
|
|
|
# Croaks (dies) if the given package name does not look like a package name. |
148
|
|
|
|
|
|
|
# Otherwise returns a cleaned form of the package name (trailing '::' are |
149
|
|
|
|
|
|
|
# removed, and '' or '::' is made into 'main'). |
150
|
|
|
|
|
|
|
sub _validate_package_name { |
151
|
0
|
|
|
0
|
|
|
my ($p) = @_; |
152
|
0
|
|
|
|
|
|
$p =~ s/::$//; |
153
|
0
|
0
|
|
|
|
|
$p = 'main' if $p eq ''; |
154
|
0
|
0
|
|
|
|
|
croak "${p} does not look like a package name" unless $p =~ m/^\w+(::\w+)*$/; |
155
|
0
|
|
|
|
|
|
return $p; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
1; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
__DATA__ |