package JE::Destroyer; our $VERSION = '0.066'; use strict; use warnings; # We cannot use JE::_FieldHash, because we end up triggering bugs in weak # references in 5.8. (JE::_FieldHash uses Tie::RefHash::Weak in 5.8.) # (Those bugs were fixed between 5.10.1 and 5.12.4, but by which commit # I wot not.) #use JE::_FieldHash; BEGIN { require constant; # unsafe_helem means that $h{$foo} will stringify $foo. # Hash::Util::FieldHash doesn’t stringify a ref key. import constant unsafe_helem => not my $hufh = eval { require Hash::Util::FieldHash }; if ($hufh) { import Hash::Util::FieldHash 'fieldhash'; } else { *fieldhash = sub {$_[0]} } } use Scalar'Util qw 'refaddr weaken'; fieldhash my %js_envs; $JE::Destroyer = 1; sub register { my $global = $_[0]->global; # if (ref ($global) eq 'JE::Scope') { use Carp; Carp::cluck; warn "-"x70, "\n" } my $globaddr = refaddr $global; if ($globaddr == refaddr $_[0]) { return } ($js_envs{unsafe_helem ? $globaddr : $global} ||= &fieldhash({})) ->{unsafe_helem ? refaddr $_[0] : $_[0]} = \(my $entry = $_[0]); weaken $entry; return # nothing; } sub destroy { exists $js_envs{$_[0]} or return; # We can’t just iterate over the values, because $$_->destroy might # actually free some of the things we are iterating over. So put them in # an array first. my @objs = values %{ $js_envs{$_[0]} }; # And still, since the values are themselves weak references to the # objects, we have to check for definition. defined $$_ and $$_->destroy for @objs; delete $js_envs{$_[0]}; $_[0]->destroy; return # nothing; } __END__ =head1 NAME JE::Destroyer - Experimental destructor for JE =head1 SYNOPSIS use JE::Destroyer; # must come first use JE; $j = new JE; # ... do stuff ... JE::Destroyer::destroy($j); # break circular refs undef $j; =head1 DESCRIPTION This is an I<experimental> module that provides a way to destroy JE objects without leaking memory. Details of its interface are subject to change drastically between releases. See the L</SYNOPSIS> above for usage. =head1 SEE ALSO L<JE>