break tracer.pl out into multifarious manifoldly marvelous modules
[gitmo/MooseX-Antlers.git] / lib / MooseX / Antlers / RefTracker.pm
1 package MooseX::Antlers::RefTracker;
2
3 use Moose;
4 use Scalar::Util qw(weaken refaddr);
5 use namespace::clean -except => 'meta';
6
7 extends 'Data::Visitor';
8
9 with 'MooseX::Antlers::Visitor::NameTracking';
10
11 # dump the lazy when we get a sensible version of D::V on the dev system
12
13 has '_traced_refs' => (is => 'ro', lazy => 1, default => sub { {} });
14 has '_traced_names' => (is => 'ro', lazy => 1, default => sub { {} });
15
16 before visit_ref => sub {
17   my ($self, $data) = @_;
18
19   # can't just rely on refaddr because it may get re-used if the data goes
20   # out of scope (we could play clever games with free magic on the wizard
21   # or whatever but KISS) - but we -can- keep a weak reference which will
22   # turn to undef if the variable disappears
23
24   weaken($self->_traced_refs->{refaddr $data} = $data);
25
26   $self->_traced_names->{refaddr $data} = $self->_current_trace_name;
27 };
28
29 sub traced_ref_map {
30   my $self = shift;
31   my $refs = $self->_traced_refs;
32   my $names = $self->_traced_names;
33
34   # nuke keys where the traced refs entry is undef since they indicate
35   # "went out of scope" so the name is no longer valid. however if we
36   # do still have a refs entry we know the name is valid because if it
37   # didn't go out of scope that refaddr can't have been re-used.
38   # (NB: I don't care if this works under ithreads)
39
40   delete @{$names}{grep !defined($refs->{$_}), keys %$names};
41   $names;
42 }
43
44 # force recursion into objects (Data::Visitor doesn't by default)
45
46 sub visit_object { shift->visit_ref(@_) }
47
48 1;