redistributing modifications to alternative method executions brings gratification
Matt S Trout [Sat, 20 Jun 2009 21:27:47 +0000 (17:27 -0400)]
tracer.pl

index a170ace..fbc08e5 100644 (file)
--- a/tracer.pl
+++ b/tracer.pl
@@ -97,49 +97,66 @@ package Ref::Replacer;
 # 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;
@@ -233,6 +250,8 @@ my $flimflam = {
 };
 
 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,