File Coverage

blib/lib/Devel/Events/Generator/Objects.pm
Criterion Covered Total %
statement 7 10 70.0
branch 3 4 75.0
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 18 77.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Devel::Events::Generator::Objects;
4              
5             my $SINGLETON;
6              
7             BEGIN {
8             # before Moose or anything else is parsed, we overload CORE::GLOBAL::bless
9             # this will divert bless to an object of our choosing if that variable is filled with something
10              
11             *CORE::GLOBAL::bless = sub {
12 10 50   10   48159 if ( defined $SINGLETON ) {
13 0         0 return $SINGLETON->bless(@_);
14             } else {
15 10         28 _core_bless(@_);
16             }
17             }
18 2     2   37918 }
19              
20             sub _core_bless {
21 10     10   15 my ( $data, $class ) = @_;
22 10 100       30 $class = caller(1) unless defined $class;
23 10         131 CORE::bless($data, $class);
24             }
25              
26 2     2   2401 use Moose;
  0         0  
  0         0  
27              
28             with qw/Devel::Events::Generator/;
29              
30             use Carp qw/croak/;
31             use Variable::Magic qw/cast getdata/;
32             use Scalar::Util qw/reftype blessed weaken/;
33              
34             use B qw/svref_2object CVf_CLONED/;
35              
36             {
37             no warnings 'redefine';
38              
39             # for some reason this breaks at compile time
40             # we need this version to preserve errors though
41             # hopefully no bad calls to bless() are made during the loading of Moose
42              
43             *_core_bless = sub {
44             my ( $data, $class ) = @_;
45             $class = caller(1) unless defined $class;
46              
47             my ( $object, $e );
48            
49             {
50             local $@;
51             $object = eval { CORE::bless($data, $class) };
52             $e = $@;
53             }
54              
55             unless ( $e ) {
56             return $object;
57             } else {
58             my $line = __LINE__ - 7;
59             my $file = quotemeta(__FILE__);
60              
61             $e =~ s/ at $file line $line\.\n$//o;
62              
63             croak($e);
64             }
65             };
66             }
67              
68             sub enable {
69             my $self = shift;
70             $SINGLETON = $self;
71             weaken($SINGLETON);
72             }
73              
74             sub disable {
75             $SINGLETON = undef;
76             }
77              
78             sub bless {
79             my ( $self, $data, $class ) = @_;
80             $class = caller(1) unless defined $class;
81              
82             my $old_class = blessed($data);
83              
84             my $object = _core_bless( $data, $class );
85              
86             require Carp::Heavy;
87             my $i = Carp::short_error_loc();
88             my ( $pkg, $file, $line ) = caller($i);
89              
90             $self->object_bless(
91             $object,
92             class => $class,
93             old_class => $old_class,
94             'package' => $pkg,
95             file => $file,
96             line => $line,
97             );
98              
99             return $object;
100             }
101              
102              
103             sub object_bless {
104             my ( $self, $object, @args ) = @_;
105              
106             my $tracked = $self->track_object($object);
107              
108             $self->send_event( object_bless => object => $object, tracked => $tracked, @args );
109             }
110              
111             sub object_destroy {
112             my ( $self, $object, @args ) = @_;
113              
114             $self->send_event( object_destroy => object => $object, @args );
115              
116             $self->untrack_object( $object );
117             }
118              
119             use constant tracker_magic => Variable::Magic::wizard(
120             free => sub {
121             my ( $object, $objs ) = @_;
122             local $@;
123             foreach my $self ( grep { defined } @{ $objs || [] } ) {
124             eval { $self->object_destroy( $object ) } # might disappear in global destruction
125             }
126             },
127             data => sub {
128             my ( $object, $self ) = @_;
129             return $self;
130             },
131             );
132              
133             sub track_object {
134             my ( $self, $object ) = @_;
135              
136              
137             my $objects;
138              
139             # blech, any idea how to clean this up?
140              
141             my $wiz = $self->tracker_magic($object);
142              
143             if ( reftype $object eq 'SCALAR' ) {
144             $objects = getdata( $$object, $wiz )
145             or cast( $$object, $wiz, ( $objects = [] ) );
146             } elsif ( reftype $object eq 'HASH' ) {
147             $objects = getdata ( %$object, $wiz )
148             or cast( %$object, $wiz, ( $objects = [] ) );
149             } elsif ( reftype $object eq 'ARRAY' ) {
150             $objects = getdata ( @$object, $wiz )
151             or cast( @$object, $wiz, ( $objects = [] ) );
152             } elsif ( reftype $object eq 'GLOB' or reftype $object eq 'IO' ) {
153             $objects = getdata ( *$object, $wiz )
154             or cast( *$object, $wiz, ( $objects = [] ) );
155             } elsif ( reftype $object eq 'CODE' ) {
156             unless ( svref_2object($object)->CvFLAGS & CVf_CLONED ) {
157             # can't track it if it never gets garbage collected
158             return;
159             } else {
160             $objects = getdata ( &$object, $wiz )
161             or cast( &$object, $wiz, ( $objects = [] ) );
162             }
163             } else {
164             die "patches welcome";
165             }
166              
167             unless ( grep { $_ eq $self } @$objects ) {
168             push @$objects, $self;
169             weaken($objects->[-1]);
170             }
171              
172             return 1;
173             }
174              
175             sub untrack_object {
176             my ( $self, $object );
177              
178             return;
179             }
180              
181              
182             __PACKAGE__;
183              
184             __END__
185              
186             =pod
187              
188             =head1 NAME
189              
190             Devel::Events::Generator::Objects - Generate events for C<bless>ing and
191             destruction of objects.
192              
193             =head1 SYNOPSIS
194              
195             use Devel::Events::Generator::Objects; # must be loaded before any code you want to instrument
196              
197             my $g = Devel::Events::Generator::Objects->new(
198             handler => $h,
199             );
200              
201             $g->enable(); # only one Objects generator may be enabled at a time
202              
203             $code->(); # objects being created and destroyed cause events to be generated
204              
205             $g->disable();
206              
207             =head1 DESCRIPTION
208              
209             This module overrides C<CORE::GLOBAL::bless> on load. The altered version will
210             delegate back to the original version until an instance of a generator is enabled.
211              
212             When a generator is enabled (only one L<Devel::Events::Generator::Objects>
213             instance may be enabled at a time. Use L<Devel::Events::Handler::Multiplex> to
214             dup events to multiple listeners), the overridden version of C<bless> will
215             cause an C<object_bless> event to fire, and will also attach magic to the
216             object to keep track of it's destruction using L<Variable::Magic>.
217              
218             When the object is freed by the interpreter an C<object_destroy> event is
219             fired. Unfortunately by this time C<perl> has already unblessed the object in
220             question, so in order to keep track of the class you must associate it yourself
221             with the reference address.
222              
223             L<Devel::Events::Handler::ObjectTracker> contains a detailed usage example.
224              
225             =head1 EVENTS
226              
227             =over 4
228              
229             =item object_bless
230              
231             When the generator is enabled, this event will fire on every call to C<bless>
232             for all code loaded after this module was loaded.
233              
234             In the future this event might omit objects created during event handling, but
235             currently it does not.
236              
237             =over 4
238              
239             =item object
240              
241             The object that was blessed
242              
243             =item old_class
244              
245             If this is a rebless then this parameter contains the class the object was in just before the bless.
246              
247             =item package
248              
249             =item file
250              
251             =item line
252              
253             These fields correspond to the location o the call to C<bless>.
254              
255             =back
256              
257             =item object_destroy
258              
259             For every object created while the generator was enabled, magic to track
260             destruction will be attached. When the object is freed this magic callback will
261             fire this event.
262              
263             =over 4
264              
265             =item object
266              
267             This field contains a reference to the object.
268              
269             B<NOTE:> by the time this callback fires the object is no longer blessed. Be
270             sure to keep track of the class of every refaddr as reported by C<object_bless>
271             in your handler if you need to know the class the object belonged to at destroy
272             time..
273              
274             =back
275              
276             =back
277              
278             =head1 METHODS
279              
280             =over 4
281              
282             =item enable
283              
284             Make this instance the enabled one (disabling any other instance which is
285             enabled).
286              
287             This only applies to the C<object_bless> method.
288              
289             =item disable
290              
291             Disable this instance. Will stop generating C<object_bless> events.
292              
293             =item bless
294              
295             The method called by the C<CORE::GLOBAL::bless> hook.
296              
297             Uses C<CORE::bless> to bless the data, and then calls C<object_bless>.
298              
299             =item object_bless
300              
301             Generates the C<object_bless> event.
302              
303             Calls C<rack_object>.
304              
305             =item object_destroy
306              
307             Generates the C<object_destroy> event.
308              
309             Calls C<untrack_object>.
310              
311             =item tracker_magic
312              
313             A class method containing the L<Variable::Magic> specification necessary for
314             L<track_object> to work.
315              
316             =item track_object
317              
318             Attach magic to an object that will call C<object_destroy> when the data is
319             about to be freed.
320              
321             =item untrack_object
322              
323             Currently empty. A subclass with a different implementation of C<track_object>
324             might want to override this.
325              
326             =back
327              
328             =head1 SEE ALSO
329              
330             L<Devel::Object::Leak>, L<Variable::Magic>
331              
332             =cut