serialising roughly the right things in roughly the right order
Matt S Trout [Mon, 22 Jun 2009 01:22:59 +0000 (21:22 -0400)]
lib/MooseX/Antlers/EvalTracker.pm
t/track_eval.t

index 68a85cc..e2cc227 100644 (file)
@@ -3,12 +3,14 @@ package MooseX::Antlers::EvalTracker;
 use Moose;
 use Scalar::Util qw(weaken refaddr);
 use PadWalker qw(closed_over);
+use Sub::Identify qw(sub_fullname);
+use B qw(perlstring);
 use namespace::clean -except => 'meta';
 
 has '_original_eval_closure' => (is => 'ro', lazy_build => 1);
 has '_our_eval_closure' => (is => 'ro', lazy_build => 1);
 
-has 'recorded_coderefs' => (is => 'ro', default => sub { {} });
+has 'recorded_coderefs' => (is => 'ro', default => sub { [] });
 
 has 'is_enabled' => (is => 'rw');
 
@@ -23,6 +25,8 @@ sub _build__our_eval_closure {
   # _eval_closure returns a CODE ref and we want that too
   sub {
     my $self = shift;
+    # ... screwed over here by $attr->default(...) and ->trigger(...)
+    # in the bloody inlined subs. XXX slap stevan, fix Moose
     # until we stop passing these let's make anything that uses it
     # crash out so at least we don't break anything
     #delete @{$_[0]}{qw($meta $attr)};
@@ -59,11 +63,40 @@ sub _install {
 
 sub _eval_closure_called_for {
   my ($self, $cr, $captures, $body) = @_;
-  use Data::Dumper; $Data::Dumper::Indent = 1;
   my %captured = %{closed_over $cr};
   delete @{$captures}{grep !exists($captured{$_}), keys %$captures};
-  warn Dumper($captures);
-  warn Dumper($body);
+  push(@{$self->recorded_coderefs}, [ $cr, $captures, $body ]);
+  #use Data::Dumper; $Data::Dumper::Indent = 1;
+  #warn Dumper($captures);
+  #warn Dumper($body);
+}
+
+sub _generate_coderef_constructor {
+  my ($self, $entry) = @_;
+  my ($cr, $captures, $body) = @{$entry};
+  my $name = sub_fullname($cr);
+  join(
+    "\n",
+    "sub {",
+    '  my $__captures = shift;',
+    $self->_generate_capture_constructor($captures),
+    'use Sub::Name ();',
+    'return *'.$name.' = Sub::Name::subname '.perlstring($name).' =>',
+    "${body};",
+    '}',
+  );
+}
+
+sub _generate_capture_constructor {
+  my ($self, $captures) = @_;
+  join(
+    "\n",
+    (map {
+      /^([\@\%\$])/ or die "capture key should start with \@, \% or \$: $_";
+      q!  my !.$_.q! = !.$1.q!{$__captures->{'!.$_.q!'}};!;
+    } keys %$captures),
+    '' # trailing \n
+  );
 }
 
 sub DEMOLISH { shift->disable }
index d81fb35..20f34ef 100644 (file)
@@ -1,6 +1,29 @@
 use strict;
 use warnings;
 use aliased 'MooseX::Antlers::EvalTracker';
+use aliased 'MooseX::Antlers::RefTracker';
+use aliased 'MooseX::Antlers::RefFilter';
+use Moose ();
+use B qw(perlstring);
+use Scalar::Util qw(refaddr);
+
+my @trap;
+
+{
+  my $orig = Moose::Meta::Class->can('add_attribute');
+  no warnings 'redefine';
+  *Moose::Meta::Class::add_attribute = sub {
+    my ($self, $name) = @_;
+    my $root_name = 
+      '$add_attribute{'.perlstring($self->name).'}'
+                   .'{'.perlstring($name).'}';
+    my $tracker = RefTracker->new({ root_name => $root_name });
+    $tracker->visit(\@_);
+    my $refs = $tracker->traced_ref_map;
+    push(@trap, [ $root_name, $name, $refs ]);
+    return $orig->(@_);
+  };
+}
 
 my $et = EvalTracker->new;
 
@@ -13,3 +36,48 @@ $et->enable;
 
   has 'test' => (is => 'rw', trigger => sub { warn "trigger!\n" });
 }
+
+use Data::Dumper; $Data::Dumper::Indent = 1;
+
+my $attr_root = '$meta->{attributes}{'.perlstring($trap[0][1]).'}';
+
+my $filter_attr = RefFilter->new({
+  external_mappings => $trap[0][2],
+  root_name => $attr_root,
+});
+
+$trap[0][2]{refaddr(Foo->meta)} = '$meta';
+my $cr = $et->recorded_coderefs->[0][0];
+$trap[0][2]{refaddr($cr)} = '$coderefs[0]';
+
+my $attr_object = Foo->meta->{attributes}{$trap[0][1]};
+
+my $record = $filter_attr->visit($attr_object);
+
+my $tracker = RefTracker->new({
+  root_name => $attr_root,
+});
+
+$tracker->visit($record);
+
+my $filter_captures = RefFilter->new({
+  external_mappings => {
+    %{$tracker->traced_ref_map}, %{$trap[0][2]},
+    refaddr($attr_object) => $attr_root,
+  },
+  root_name => '$captures',
+});
+
+my $captures_record = $filter_captures->visit($et->recorded_coderefs->[0][1]);
+
+warn Data::Dumper->Dump([$record],[$attr_root]);
+
+warn 'my '.Data::Dumper->Dump([$captures_record],['$captures']);
+
+warn $filter_captures->fixup_code;
+
+warn 'my @coderefs = ('."\n".$et->_generate_coderef_constructor($et->recorded_coderefs->[0])."->(\$captures)\n);\n";
+
+warn $filter_attr->fixup_code;
+
+#warn %{$trap[0]};