# as yet
use Moose;
-# ensure we have next::method available - just because Moose loads it
-# as a side effect doesn't mean it's clever to rely on that
-use MRO::Compat ();
use Scalar::Util qw(refaddr isweak);
use namespace::clean -except => 'meta';
extends 'Data::Visitor';
-with 'Visitor::NameTracking';
+# we need name tracking but have to apply the role at the end of the file
+# so that our around modifiers end up within the name tracking around
+# instead of outside - otherwise e.g. array value weakening goes wrong
has 'external_mappings' => (is => 'ro', lazy => 1, default => sub { {} });
has 'weaken_these' => (is => 'ro', lazy => 1, default => sub { {} });
has 'map_these' => (is => 'ro', lazy => 1, default => sub { {} });
-# fairly sure an around modifier will get severely fucked up here
-# in that the copying of @_ will lose the weakness we need to check
-
-sub visit_ref {
- my $self = shift;
-
- # have to test $_[0] directly since copying a weak ref gives a strong ref
-
-warn $self->_current_trace_name;
-warn $_[0];
- if (isweak $_[0]) {
-warn "got here";
- $self->weaken_these->{$self->_current_trace_name} = 1;
- }
+around visit_ref => sub {
+ my ($orig, $self) = (shift, shift);
+ my $value = $_[0];
# if we've got a mapping for a reference (i.e. it's supplied from
# somewhere else) then we need to record where we are and then
# return undef for the fmap process so we serialize an undefined
# value and the fixup puts the external reference back in later
- if (my $m = $self->external_mappings->{refaddr $_[0]}) {
+ if (my $m = $self->external_mappings->{refaddr $value}) {
$self->map_these->{$self->_current_trace_name} = $m;
return undef;
}
- return $self->next::method(@_);
-}
+ return $self->$orig(@_);
+};
+
+around visit_hash_value => sub {
+ my ($orig, $self) = (shift, shift);
+ my ($value, $key, $hash) = @_;
+ if (isweak $hash->{$key}) {
+ $self->weaken_these->{$self->_current_trace_name} = 1;
+ }
+ return $self->$orig(@_);
+};
-sub _register_mapping { $_[2] }
+around visit_array_entry => sub {
+ my ($orig, $self) = (shift, shift);
+ my ($value, $index, $array) = @_;
+ if (isweak $array->[$index]) {
+ $self->weaken_these->{$self->_current_trace_name} = 1;
+ }
+ return $self->$orig(@_);
+};
+
+around visit_scalar => sub {
+ my ($orig, $self) = (shift, shift);
+ my $scalar = $_[0];
+ if (isweak $$scalar) {
+ $self->weaken_these->{$self->_current_trace_name} = 1;
+ }
+ return $self->$orig(@_);
+};
+
+# now it's safe to apply the role
+
+with 'Visitor::NameTracking';
sub fixup_code {
my $self = shift;
};
weaken($flimflam->{weak_one} = $flimflam->{one});
+weaken($flimflam->{weak_member}[0] = $flimflam->{bard});
+weaken(${$flimflam->{weak_scalar}} = $flimflam->{bard_guts});
my $replacer = Ref::Replacer->new({
external_mappings => $result,