Slides for the talk the-devils-repl at lpw-2010
The
Devil's
REPL
-
5 minutes
65 slides
1 crack pipe
-
Devel::REPL
-
written for
a series
of blog posts
-
Moose
MooseX::Object::Pluggable
-
Works
great
-
FATTER
THAN
CARTMAN
-
Shitloads
of XS
-
FAIL
-
Minimalist
repl?
-
Pure perl
repl!
-
But I need
lexical
persistence
-
Lexical::Persistence
-
PadWalker
Devel::LexAlias
-
Hmm ...
-
eval q{sub {
my $x; $x++; $x
}}
-
use B;
my $obj = svref_2object($sub);
-
$obj->PADLIST
-
$obj->PADLIST->ARRAY
-
[
'B::SPECIAL=SCALAR(0x1a47510)',
'B::PVNV=SCALAR(0x1a47570)',
'B::SPECIAL=SCALAR(0x1a475d0)'
],
[
'B::AV=SCALAR(0x1a47678)',
'B::IV=SCALAR(0x1a476a8)',
'B::NULL=SCALAR(0x1a47708)'
]
-
[ \@names,
\@values ]
-
map $_->PV, grep $_->isa('B::PV'),
$obj->PADLIST->ARRAYelt(0)->ARRAY;
-
Values are
useless
-
Values are
already
DESTROYed
-
Hmm ...
-
BEGIN { our $y = \$x; }
-
Works at
toplevel
-
sub {
...
BEGIN { our $y = \$x; }
}
-
Variable "$x"
is not
available
-
:(
-
sub {
...
capture_pads()
}
-
Breaks
return
values
-
Hmm ...
-
sub {
my $guard = bless({}, 'HasADESTROY');
...
}
-
Lexicals
are freed
in reverse
order of
declaration
-
So $guard
is DESTROYed
too late
-
:(
-
Hmm ...
-
sub {
...
sub aha { our $y = \$x }
}
-
Variable $x
will not
stay shared
-
... *stay* ...
-
I only need
to share it
the once!
-
Now how do I
get the list?
-
sub {
...
sub inside { }
BEGIN { inspect_inside() }
...
-
my $obj = svref_2object(
\&inside
);
-
map $_->PV, grep $_->isa('B::PV'),
$obj->PADLIST->ARRAYelt(0)->ARRAY;
-
But how do
I inject the
capture code?
-
sub {
...
sub inside { }
BEGIN { inspect_inside() }
__NOW_CAPTURIZE__
-
s{__NOW_CAPTURIZE__}
{$capture_code}
-
Source
filter?
-
Source filters
don't work
in string eval
-
:(
-
But ...
-
coderef
in @INC
-
... opening
a string
filehandle
-
local @INC = (sub {
if ($_[1] eq '/eval_do') {
open my $fh, '<', $text_ref;
return $fh;
}
}, @INC);
-
The BEGIN
happens
before that
line's read
-
And my s{}{}
will work!
-
Or even an
append
-
$code .= '+{ '
.join(', ',
map "'$_' => \\$_", @names
)
.' };'
-
my $x = ${$_[2]->{"\$x"}};
sub Eval::WithLexicals::Cage::current_line {
package Eval::WithLexicals::Scratchpad;
++$x
;sub Eval::WithLexicals::Cage::pad_capture { }
BEGIN { Eval::WithLexicals::Util::capture_list() }
sub Eval::WithLexicals::Cage::grab_captures {
no warnings 'closure';
package Eval::WithLexicals::Cage;+{ '$x' => \$x };
}
}
-
my $eval = Eval::WithLexicals->new;
my $read = Term::ReadLine->new('Perl REPL');
while (1) {
my $line = $read->readline('re.pl$ ');
exit unless defined $line;
my @ret; try {
local $SIG{INT} = sub { die "Caught SIGINT" };
@ret = $eval->eval($line);
} catch {
@ret = ("Error!", $_);
};
print Dumper @ret;
}
-
re.pl$ my $x = 1
1
re.pl$ ++$x
2
re.pl$ $x
2
-
Eval::WithLexicals
went to CPAN during
the LPW hackathon
-
$ cpanm Eval::WithLexicals
$ tinyrepl
re.pl$
-
Thank you.