| Devel-Events-Objects documentation | Contained in the Devel-Events-Objects distribution. |
Devel::Events::Generator::Objects - Generate events for blessing and
destruction of objects.
use Devel::Events::Generator::Objects; # must be loaded before any code you want to instrument my $g = Devel::Events::Generator::Objects->new( handler => $h, ); $g->enable(); # only one Objects generator may be enabled at a time $code->(); # objects being created and destroyed cause events to be generated $g->disable();
This module overrides CORE::GLOBAL::bless on load. The altered version will
delegate back to the original version until an instance of a generator is enabled.
When a generator is enabled (only one Devel::Events::Generator::Objects
instance may be enabled at a time. Use Devel::Events::Handler::Multiplex to
dup events to multiple listeners), the overridden version of bless will
cause an object_bless event to fire, and will also attach magic to the
object to keep track of it's destruction using Variable::Magic.
When the object is freed by the interpreter an object_destroy event is
fired. Unfortunately by this time perl has already unblessed the object in
question, so in order to keep track of the class you must associate it yourself
with the reference address.
Devel::Events::Handler::ObjectTracker contains a detailed usage example.
When the generator is enabled, this event will fire on every call to bless
for all code loaded after this module was loaded.
In the future this event might omit objects created during event handling, but currently it does not.
The object that was blessed
If this is a rebless then this parameter contains the class the object was in just before the bless.
These fields correspond to the location o the call to bless.
For every object created while the generator was enabled, magic to track destruction will be attached. When the object is freed this magic callback will fire this event.
This field contains a reference to the object.
NOTE: by the time this callback fires the object is no longer blessed. Be
sure to keep track of the class of every refaddr as reported by object_bless
in your handler if you need to know the class the object belonged to at destroy
time..
Make this instance the enabled one (disabling any other instance which is enabled).
This only applies to the object_bless method.
Disable this instance. Will stop generating object_bless events.
The method called by the CORE::GLOBAL::bless hook.
Uses CORE::bless to bless the data, and then calls object_bless.
Generates the object_bless event.
Calls rack_object.
Generates the object_destroy event.
Calls untrack_object.
A class method containing the Variable::Magic specification necessary for track_object to work.
Attach magic to an object that will call object_destroy when the data is
about to be freed.
Currently empty. A subclass with a different implementation of track_object
might want to override this.
Devel::Object::Leak, Variable::Magic
| Devel-Events-Objects documentation | Contained in the Devel-Events-Objects distribution. |
#!/usr/bin/perl package Devel::Events::Generator::Objects; my $SINGLETON; BEGIN { # before Moose or anything else is parsed, we overload CORE::GLOBAL::bless # this will divert bless to an object of our choosing if that variable is filled with something *CORE::GLOBAL::bless = sub { if ( defined $SINGLETON ) { return $SINGLETON->bless(@_); } else { _core_bless(@_); } } } sub _core_bless { my ( $data, $class ) = @_; $class = caller(1) unless defined $class; CORE::bless($data, $class); } use Moose; with qw/Devel::Events::Generator/; use Carp qw/croak/; use Variable::Magic qw/cast getdata/; use Scalar::Util qw/reftype blessed weaken/; use B qw/svref_2object CVf_CLONED/; { no warnings 'redefine'; # for some reason this breaks at compile time # we need this version to preserve errors though # hopefully no bad calls to bless() are made during the loading of Moose *_core_bless = sub { my ( $data, $class ) = @_; $class = caller(1) unless defined $class; my ( $object, $e ); { local $@; $object = eval { CORE::bless($data, $class) }; $e = $@; } unless ( $e ) { return $object; } else { my $line = __LINE__ - 7; my $file = quotemeta(__FILE__); $e =~ s/ at $file line $line\.\n$//o; croak($e); } }; } sub enable { my $self = shift; $SINGLETON = $self; weaken($SINGLETON); } sub disable { $SINGLETON = undef; } sub bless { my ( $self, $data, $class ) = @_; $class = caller(1) unless defined $class; my $old_class = blessed($data); my $object = _core_bless( $data, $class ); require Carp::Heavy; my $i = Carp::short_error_loc(); my ( $pkg, $file, $line ) = caller($i); $self->object_bless( $object, class => $class, old_class => $old_class, 'package' => $pkg, file => $file, line => $line, ); return $object; } sub object_bless { my ( $self, $object, @args ) = @_; my $tracked = $self->track_object($object); $self->send_event( object_bless => object => $object, tracked => $tracked, @args ); } sub object_destroy { my ( $self, $object, @args ) = @_; $self->send_event( object_destroy => object => $object, @args ); $self->untrack_object( $object ); } use constant tracker_magic => Variable::Magic::wizard( free => sub { my ( $object, $objs ) = @_; local $@; foreach my $self ( grep { defined } @{ $objs || [] } ) { eval { $self->object_destroy( $object ) } # might disappear in global destruction } }, data => sub { my ( $object, $self ) = @_; return $self; }, ); sub track_object { my ( $self, $object ) = @_; my $objects; # blech, any idea how to clean this up? my $wiz = $self->tracker_magic($object); if ( reftype $object eq 'SCALAR' ) { $objects = getdata( $$object, $wiz ) or cast( $$object, $wiz, ( $objects = [] ) ); } elsif ( reftype $object eq 'HASH' ) { $objects = getdata ( %$object, $wiz ) or cast( %$object, $wiz, ( $objects = [] ) ); } elsif ( reftype $object eq 'ARRAY' ) { $objects = getdata ( @$object, $wiz ) or cast( @$object, $wiz, ( $objects = [] ) ); } elsif ( reftype $object eq 'GLOB' or reftype $object eq 'IO' ) { $objects = getdata ( *$object, $wiz ) or cast( *$object, $wiz, ( $objects = [] ) ); } elsif ( reftype $object eq 'CODE' ) { unless ( svref_2object($object)->CvFLAGS & CVf_CLONED ) { # can't track it if it never gets garbage collected return; } else { $objects = getdata ( &$object, $wiz ) or cast( &$object, $wiz, ( $objects = [] ) ); } } else { die "patches welcome"; } unless ( grep { $_ eq $self } @$objects ) { push @$objects, $self; weaken($objects->[-1]); } return 1; } sub untrack_object { my ( $self, $object ); return; } __PACKAGE__; __END__