Add persistent hints
[p5sagit/Eval-WithLexicals.git] / lib / Eval / WithLexicals / WithHintPersistence.pm
1 package Eval::WithLexicals::WithHintPersistence;
2 use Moo::Role;
3
4 our($hints, %hints);
5
6 has first_eval => (
7   is => 'rw',
8   default => sub { 1 },
9 );
10
11 has hints => (
12   is => 'rw',
13   default => sub { {} },
14 );
15
16 around eval => sub {
17   my $orig = shift;
18   my($self) = @_;
19
20   local *Eval::WithLexicals::Cage::capture_hints;
21   local $Eval::WithLexicals::Cage::hints = { %{$self->hints} };
22
23   my @ret = $orig->(@_);
24
25   $self->hints({ Eval::WithLexicals::Cage::capture_hints() });
26
27   @ret;
28 };
29
30 # XXX: Sub::Quote::capture_unroll without 'my'
31 use B();
32 sub _capture_unroll_global {
33   my ($from, $captures, $indent) = @_;
34   join(
35     '',
36     map {
37       /^([\@\%\$])/
38         or die "capture key should start with \@, \% or \$: $_";
39       (' ' x $indent).qq{${_} = ${1}{${from}->{${\B::perlstring $_}}};\n};
40     } keys %$captures
41   );
42 }
43
44 sub setup_code {
45   my($self) = @_;
46   # Only run the prelude on the first eval, hints will be set after
47   # that.
48   if($self->first_eval) {
49     $self->first_eval(0);
50     return $self->prelude;
51   } else {
52     # Seems we can't use the technique of passing via @_ for code in a BEGIN
53     # block
54     return q[ BEGIN { ],
55       _capture_unroll_global('$Eval::WithLexicals::Cage::hints', $self->hints, 2),
56       q[ } ],
57   }
58 };
59
60 around capture_code => sub {
61   my $orig = shift;
62   my($self) = @_;
63
64   ( q{ sub Eval::WithLexicals::Cage::capture_hints {
65           no warnings 'closure'; # XXX: can we limit the scope of this?
66           my($hints, %hints);
67           BEGIN { $hints = $^H; %hints = %^H; }
68           return q{$^H} => \$hints, q{%^H} => \%hints;
69         } },
70     $orig->(@_) )
71 };
72
73 1;