Get tests working under `prove -lrswTj10`
[p5sagit/Class-Accessor-Grouped.git] / lib / Class / Accessor / Grouped.pm
index 5fe4c3d..f2c0ea8 100644 (file)
@@ -15,12 +15,12 @@ BEGIN {
   }
 }
 
-our $VERSION = '0.10006';
+our $VERSION = '0.10010';
 $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
 
 # when changing minimum version don't forget to adjust Makefile.PL as well
 our $__minimum_xsa_version;
-BEGIN { $__minimum_xsa_version = '1.13' }
+BEGIN { $__minimum_xsa_version = '1.15' }
 
 our $USE_XS;
 # the unless defined is here so that we can override the value
@@ -29,7 +29,8 @@ $USE_XS = $ENV{CAG_USE_XS}
   unless defined $USE_XS;
 
 BEGIN {
-  package __CAG_ENV__;
+  package # hide from PAUSE
+    __CAG_ENV__;
 
   die "Huh?! No minimum C::XSA version?!\n"
     unless $__minimum_xsa_version;
@@ -43,9 +44,22 @@ BEGIN {
     Module::Runtime::require_module('Sub::Name')
   } ? 0 : "$@" );
 
-  constant->import( NO_CXSA => ( !NO_SUBNAME() and eval {
-    Module::Runtime::use_module('Class::XSAccessor' => $__minimum_xsa_version)
-  } ) ? 0 : "$@" );
+  my $found_cxsa;
+  constant->import( NO_CXSA => ( NO_SUBNAME() || ( eval {
+    Module::Runtime::require_module('Class::XSAccessor');
+    $found_cxsa = Class::XSAccessor->VERSION;
+    Class::XSAccessor->VERSION($__minimum_xsa_version);
+  } ? 0 : "$@" ) ) );
+
+  if (NO_CXSA() and $found_cxsa and !$ENV{CAG_OLD_XS_NOWARN}) {
+    warn(
+      'The installed version of Class::XSAccessor is too old '
+    . "(v$found_cxsa < v$__minimum_xsa_version). Please upgrade "
+    . "to instantly quadruple the performance of 'simple' accessors. "
+    . 'Set $ENV{CAG_OLD_XS_NOWARN} if you wish to disable this '
+    . "warning.\n"
+    );
+  }
 
   constant->import( BROKEN_GOTO => ($] < '5.008009') ? 1 : 0 );
 
@@ -54,17 +68,27 @@ BEGIN {
   constant->import( TRACK_UNDEFER_FAIL => (
     $INC{'Test/Builder.pm'} || $INC{'Test/Builder2.pm'}
       and
-    $0 =~ m|^ x?t / .+ \.t $|x
+    $0 =~ m{ ^ (?: \. \/ )? x?t / .+ \.t $}x
   ) ? 1 : 0 );
+
+  require B;
+  # a perl 5.6 kludge
+  unless (B->can('perlstring')) {
+    require Data::Dumper;
+    my $d = Data::Dumper->new([])->Indent(0)->Purity(0)->Pad('')->Useqq(1)->Terse(1)->Freezer('')->Toaster('');
+    *B::perlstring = sub { $d->Values([shift])->Dump };
+  }
 }
 
 # Yes this method is undocumented
 # Yes it should be a private coderef like all the rest at the end of this file
 # No we can't do that (yet) because the DBIC-CDBI compat layer overrides it
 # %$*@!?&!&#*$!!!
+
+my $illegal_accessors_warned;
 sub _mk_group_accessors {
   my($self, $maker, $group, @fields) = @_;
-  my $class = Scalar::Util::blessed $self || $self;
+  my $class = length (ref ($self) ) ? ref ($self) : $self;
 
   no strict 'refs';
   no warnings 'redefine';
@@ -76,21 +100,59 @@ sub _mk_group_accessors {
 
     my ($name, $field) = (ref $_) ? (@$_) : ($_, $_);
 
-    for (qw/DESTROY AUTOLOAD CLONE/) {
-      Carp::carp("Having a data accessor named '$name' in '$class' is unwise.")
-        if $name eq $_;
+    if ($name !~ /\A[A-Z_a-z][0-9A-Z_a-z]*\z/) {
+
+      if ($name =~ /\0/) {
+        Carp::croak(sprintf
+          "Illegal accessor name %s - nulls should never appear in stash keys",
+          B::perlstring($name),
+        );
+      }
+      elsif (! $ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK} ) {
+        Carp::croak(
+          "Illegal accessor name '$name'. If you want CAG to attempt creating "
+        . 'it anyway (possible if Sub::Name is available) set '
+        . '$ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK}'
+        );
+      }
+      elsif (__CAG_ENV__::NO_SUBNAME) {
+        Carp::croak(
+          "Unable to install accessor with illegal name '$name': "
+        . 'Sub::Name not available'
+        );
+      }
+      elsif (
+        # Because one of the former maintainers of DBIC::SL is a raging
+        # idiot, there is now a ton of DBIC code out there that attempts
+        # to create column accessors with illegal names. In the interest
+        # of not cluttering the logs of unsuspecting victims (unsuspecting
+        # because these accessors are unusuable anyway) we provide an
+        # explicit "do not warn at all" escape, until all such code is
+        # fixed (this will be a loooooong time >:(
+        $ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK} ne 'DO_NOT_WARN'
+          and
+        ! $illegal_accessors_warned->{$class}++
+      ) {
+        Carp::carp(
+          "Installing illegal accessor '$name' into $class, see "
+        . 'documentation for more details'
+        );
+      }
     }
 
+    Carp::carp("Having a data accessor named '$name' in '$class' is unwise.")
+      if $name =~ /\A(?: DESTROY | AUTOLOAD | CLONE )\z/x;
+
     my $alias = "_${name}_accessor";
 
-    for my $meth ($name, $alias) {
+    for ($name, $alias) {
 
       # the maker may elect to not return anything, meaning it already
       # installed the coderef for us (e.g. lack of Sub::Name)
-      my $cref = $self->$maker($group, $field, $meth)
+      my $cref = $self->$maker($group, $field, $_)
         or next;
 
-      my $fq_meth = "${class}::${meth}";
+      my $fq_meth = "${class}::$_";
 
       *$fq_meth = Sub::Name::subname($fq_meth, $cref);
         #unless defined &{$class."\:\:$field"}
@@ -98,7 +160,7 @@ sub _mk_group_accessors {
   }
 };
 
-# coderef is setup at the end for clarity
+# $gen_accessor coderef is setup at the end for clarity
 my $gen_accessor;
 
 =head1 NAME
@@ -115,10 +177,50 @@ Class::Accessor::Grouped - Lets you build groups of accessors
  # make accessor that works for objects and classes
  __PACKAGE__->mk_group_accessors(inherited => 'awesome_level');
 
+ # make an accessor which calls a custom pair of getters/setters
+ sub get_column { ... this will be called when you do $obj->name() ... }
+ sub set_column { ... this will be called when you do $obj->name('foo') ... }
+ __PACKAGE__->mk_group_accessors(column => 'name');
+
 =head1 DESCRIPTION
 
 This class lets you build groups of accessors that will call different
-getters and setters.
+getters and setters. The documentation of this module still requires a lot
+of work (B<< volunteers welcome >.> >>), but in the meantime you can refer to
+L<this post|http://lo-f.at/glahn/2009/08/WritingPowerfulAccessorsForPerlClasses.html>
+for more information.
+
+=head2 Notes on accessor names
+
+In general method names in Perl are considered identifiers, and as such need to
+conform to the identifier specification of C<qr/\A[A-Z_a-z][0-9A-Z_a-z]*\z/>.
+While it is rather easy to invoke methods with non-standard names
+(C<< $obj->${\"anything goes"} >>), it is not possible to properly declare such
+methods without the use of L<Sub::Name>. Since this module must be able to
+function identically with and without its optional dependencies, starting with
+version C<0.10008> attempting to declare an accessor with a non-standard name
+is a fatal error (such operations would silently succeed since version
+C<0.08004>, as long as L<Sub::Name> is present, or otherwise would result in a
+syntax error during a string eval).
+
+Unfortunately in the years since C<0.08004> a rather large body of code
+accumulated in the wild that does attempt to declare accessors with funny
+names. One notable perpetrator is L<DBIx::Class::Schema::Loader>, which under
+certain conditions could create accessors of the C<column> group which start
+with numbers and/or some other punctuation (the proper way would be to declare
+columns with the C<accessor> attribute set to C<undef>).
+
+Therefore an escape mechanism is provided via the environment variable
+C<CAG_ILLEGAL_ACCESSOR_NAME_OK>. When set to a true value, one warning is
+issued B<per class> on attempts to declare an accessor with a non-conforming
+name, and as long as L<Sub::Name> is available all accessors will be properly
+created. Regardless of this setting, accessor names containing nulls C<"\0">
+are disallowed, due to various deficiencies in perl itself.
+
+If your code base has too many instances of illegal accessor declarations, and
+a fix is not feasible due to time constraints, it is possible to disable the
+warnings altogether by setting C<$ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK}> to
+C<DO_NOT_WARN> (observe capitalization).
 
 =head1 METHODS
 
@@ -178,6 +280,7 @@ sub mk_group_ro_accessors {
   my($self, $group, @fields) = @_;
 
   $self->_mk_group_accessors('make_group_ro_accessor', $group, @fields);
+  return;
 }
 
 =head2 mk_group_wo_accessors
@@ -202,6 +305,7 @@ sub mk_group_wo_accessors {
   my($self, $group, @fields) = @_;
 
   $self->_mk_group_accessors('make_group_wo_accessor', $group, @fields);
+  return;
 }
 
 =head2 get_simple
@@ -220,7 +324,7 @@ name passed as an argument.
 =cut
 
 sub get_simple {
-  return $_[0]->{$_[1]};
+  $_[0]->{$_[1]};
 }
 
 =head2 set_simple
@@ -239,7 +343,7 @@ for the field name passed as an argument.
 =cut
 
 sub set_simple {
-  return $_[0]->{$_[1]} = $_[2];
+  $_[0]->{$_[1]} = $_[2];
 }
 
 
@@ -262,25 +366,25 @@ instances.
 =cut
 
 sub get_inherited {
-  my $class;
-
-  if ( defined( $class = Scalar::Util::blessed $_[0] ) ) {
+  if ( length (ref ($_[0]) ) ) {
     if (Scalar::Util::reftype $_[0] eq 'HASH') {
       return $_[0]->{$_[1]} if exists $_[0]->{$_[1]};
+      # everything in @_ is aliased, an assignment won't work
+      splice @_, 0, 1, ref($_[0]);
     }
     else {
       Carp::croak('Cannot get inherited value on an object instance that is not hash-based');
     }
   }
-  else {
-    $class = $_[0];
-  }
 
+  # if we got this far there is nothing in the instance
+  # OR this is a class call
+  # in any case $_[0] contains the class name (see splice above)
   no strict 'refs';
   no warnings 'uninitialized';
 
   my $cag_slot = '::__cag_'. $_[1];
-  return ${$class.$cag_slot} if defined(${$class.$cag_slot});
+  return ${$_[0].$cag_slot} if defined(${$_[0].$cag_slot});
 
   do { return ${$_.$cag_slot} if defined(${$_.$cag_slot}) }
     for $_[0]->get_super_paths;
@@ -309,17 +413,16 @@ hash-based object.
 =cut
 
 sub set_inherited {
-  if (defined Scalar::Util::blessed $_[0]) {
+  if (length (ref ($_[0]) ) ) {
     if (Scalar::Util::reftype $_[0] eq 'HASH') {
       return $_[0]->{$_[1]} = $_[2];
     } else {
       Carp::croak('Cannot set inherited value on an object instance that is not hash-based');
     };
-  } else {
-    no strict 'refs';
+  }
 
-    return ${$_[0].'::__cag_'.$_[1]} = $_[2];
-  };
+  no strict 'refs';
+  ${$_[0].'::__cag_'.$_[1]} = $_[2];
 }
 
 =head2 get_component_class
@@ -344,7 +447,7 @@ Gets the value of the specified component class.
 =cut
 
 sub get_component_class {
-  return $_[0]->get_inherited($_[1]);
+  $_[0]->get_inherited($_[1]);
 };
 
 =head2 set_component_class
@@ -389,7 +492,7 @@ sub set_component_class {
     }
   };
 
-  return $_[0]->set_inherited($_[1], $_[2]);
+  $_[0]->set_inherited($_[1], $_[2]);
 };
 
 =head1 INTERNAL METHODS
@@ -485,26 +588,51 @@ accessors if this module is available on your system.
 
 =head2 Benchmark
 
-This is the result of a set/get/set loop benchmark on perl 5.12.1 with
-thread support, showcasing most popular accessor builders: L<Moose>, L<Mouse>,
-L<Moo>, L<CAF|Class::Accessor::Fast>, L<CAF_XS|Class::Accessor::Fast::XS>,
-L<XSA|Class::XSAccessor>, and L<CAF_XSA|Class::XSAccessor::Compat>:
-
-           Rate  CAG moOse  CAF moUse  moo HANDMADE CAF_XS moUse_XS moo_XS CAF_XSA  XSA CAG_XS
- CAG      169/s   --  -21% -24%  -32% -32%     -34%   -59%     -63%   -67%    -67% -67%   -67%
- moOse    215/s  27%    --  -3%  -13% -13%     -15%   -48%     -53%   -58%    -58% -58%   -58%
- CAF      222/s  31%    3%   --  -10% -10%     -13%   -46%     -52%   -57%    -57% -57%   -57%
- moUse    248/s  46%   15%  11%    --  -0%      -3%   -40%     -46%   -52%    -52% -52%   -52%
- moo      248/s  46%   15%  11%    0%   --      -3%   -40%     -46%   -52%    -52% -52%   -52%
- HANDMADE 255/s  50%   18%  14%    3%   3%       --   -38%     -45%   -50%    -51% -51%   -51%
- CAF_XS   411/s 143%   91%  85%   66%  66%      61%     --     -11%   -20%    -20% -21%   -21%
- moUse_XS 461/s 172%  114% 107%   86%  86%      81%    12%       --   -10%    -11% -11%   -11%
- moo_XS   514/s 204%  139% 131%  107% 107%     102%    25%      12%     --     -0%  -1%    -1%
- CAF_XSA  516/s 205%  140% 132%  108% 108%     103%    26%      12%     0%      --  -0%    -0%
- XSA      519/s 206%  141% 133%  109% 109%     104%    26%      13%     1%      0%   --    -0%
- CAG_XS   519/s 206%  141% 133%  109% 109%     104%    26%      13%     1%      0%   0%     --
-
-Benchmark program is available in the root of the
+This is the benchmark of 200 get/get/set/get/set cycles on perl 5.16.2 with
+thread support, showcasing how this modules L<simple (CAG_S)|/get_simple>,
+L<inherited (CAG_INH)|/get_inherited> and L<inherited with parent-class data
+(CAG_INHP)|/get_inherited> accessors stack up against most popular accessor 
+builders:  L<Moose>, L<Moo>, L<Mo>, L<Mouse> (both pure-perl and XS variant),
+L<Object::Tiny::RW (OTRW)|Object::Tiny::RW>,
+L<Class::Accessor (CA)|Class::Accessor>,
+L<Class::Accessor::Lite (CAL)|Class::Accessor::Lite>,
+L<Class::Accessor::Fast (CAF)|Class::Accessor::Fast>,
+L<Class::Accessor::Fast::XS (CAF_XS)|Class::Accessor::Fast::XS>
+and L<Class::XSAccessor (XSA)|Class::XSAccessor>
+
+                      Rate CAG_INHP CAG_INH     CA  CAG_S    CAF  moOse   OTRW    CAL     mo  moUse HANDMADE    moo CAF_XS moUse_XS    XSA
+
+ CAG_INHP  287.021+-0.02/s       --   -0.3% -10.0% -37.1% -53.1% -53.6% -53.7% -54.1% -56.9% -59.0%   -59.6% -59.8% -78.7%   -81.9% -83.5%
+
+ CAG_INH  288.025+-0.031/s     0.3%      --  -9.7% -36.9% -52.9% -53.5% -53.5% -53.9% -56.7% -58.8%   -59.5% -59.7% -78.6%   -81.9% -83.5%
+
+ CA       318.967+-0.047/s    11.1%   10.7%     -- -30.1% -47.9% -48.5% -48.5% -49.0% -52.1% -54.4%   -55.1% -55.3% -76.3%   -79.9% -81.7%
+
+ CAG_S    456.107+-0.054/s    58.9%   58.4%  43.0%     -- -25.4% -26.3% -26.4% -27.0% -31.5% -34.8%   -35.8% -36.1% -66.1%   -71.3% -73.9%
+
+ CAF      611.745+-0.099/s   113.1%  112.4%  91.8%  34.1%     --  -1.2%  -1.2%  -2.1%  -8.1% -12.6%   -14.0% -14.3% -54.5%   -61.5% -64.9%
+
+ moOse    619.051+-0.059/s   115.7%  114.9%  94.1%  35.7%   1.2%     --  -0.1%  -1.0%  -7.0% -11.6%   -12.9% -13.3% -54.0%   -61.0% -64.5%
+
+ OTRW       619.475+-0.1/s   115.8%  115.1%  94.2%  35.8%   1.3%   0.1%     --  -0.9%  -6.9% -11.5%   -12.9% -13.2% -54.0%   -61.0% -64.5%
+
+ CAL      625.106+-0.085/s   117.8%  117.0%  96.0%  37.1%   2.2%   1.0%   0.9%     --  -6.1% -10.7%   -12.1% -12.5% -53.5%   -60.6% -64.2%
+
+ mo         665.44+-0.12/s   131.8%  131.0% 108.6%  45.9%   8.8%   7.5%   7.4%   6.5%     --  -4.9%    -6.4%  -6.8% -50.5%   -58.1% -61.9%
+
+ moUse       699.9+-0.15/s   143.9%  143.0% 119.4%  53.5%  14.4%  13.1%  13.0%  12.0%   5.2%     --    -1.6%  -2.0% -48.0%   -55.9% -59.9%
+
+ HANDMADE   710.98+-0.16/s   147.7%  146.8% 122.9%  55.9%  16.2%  14.9%  14.8%  13.7%   6.8%   1.6%       --  -0.4% -47.2%   -55.2% -59.2%
+
+ moo        714.04+-0.13/s   148.8%  147.9% 123.9%  56.6%  16.7%  15.3%  15.3%  14.2%   7.3%   2.0%     0.4%     -- -46.9%   -55.0% -59.1%
+
+ CAF_XS   1345.55+-0.051/s   368.8%  367.2% 321.8% 195.0% 120.0% 117.4% 117.2% 115.3% 102.2%  92.2%    89.3%  88.4%     --   -15.3% -22.9%
+
+ moUse_XS    1588+-0.036/s   453.3%  451.3% 397.9% 248.2% 159.6% 156.5% 156.3% 154.0% 138.6% 126.9%   123.4% 122.4%  18.0%       --  -9.0%
+
+ XSA      1744.67+-0.052/s   507.9%  505.7% 447.0% 282.5% 185.2% 181.8% 181.6% 179.1% 162.2% 149.3%   145.4% 144.3%  29.7%     9.9%     --
+
+Benchmarking program is available in the root of the
 L<repository|http://search.cpan.org/dist/Class-Accessor-Grouped/>:
 
 =head2 Notes on Class::XSAccessor
@@ -569,62 +697,87 @@ if (! defined $USE_XS) {
   $xsa_autodetected++;
 }
 
+
 my $maker_templates = {
   rw => {
-    xs_call => 'accessors',
-    pp_code => sub {
-      my $set = "set_$_[0]";
-      my $get = "get_$_[0]";
-      my $field = $_[1];
-      $field =~ s/'/\\'/g;
-
-      "
-        \@_ != 1
-          ? shift->$set('$field', \@_)
-          : shift->$get('$field')
-      "
+    cxsa_call => 'accessors',
+    pp_generator => sub {
+      # my ($group, $fieldname) = @_;
+      my $quoted_fieldname = B::perlstring($_[1]);
+      sprintf <<'EOS', ($_[0], $quoted_fieldname) x 2;
+
+@_ > 1
+  ? shift->set_%s(%s, @_)
+  : shift->get_%s(%s)
+EOS
+
     },
   },
   ro => {
-    xs_call => 'getters',
-    pp_code => sub {
-      my $get = "get_$_[0]";
-      my $field = $_[1];
-      $field =~ s/'/\\'/g;
-
-      "
-        \@_ == 1
-          ? shift->$get('$field')
-          : do {
-            my \$caller = caller;
-            my \$class = ref \$_[0] || \$_[0];
-            Carp::croak(\"'\$caller' cannot alter the value of '$field' \".
-                        \"(read-only attributes of class '\$class')\");
-          }
-      "
+    cxsa_call => 'getters',
+    pp_generator => sub {
+      # my ($group, $fieldname) = @_;
+      my $quoted_fieldname = B::perlstring($_[1]);
+      sprintf  <<'EOS', $_[0], $quoted_fieldname;
+
+@_ > 1
+  ? do {
+    my ($meth) = (caller(0))[3] =~ /([^\:]+)$/;
+    my $class = length( ref($_[0]) ) ? ref($_[0]) : $_[0];
+    Carp::croak(
+      "'$meth' cannot alter its value (read-only attribute of class $class)"
+    );
+  }
+  : shift->get_%s(%s)
+EOS
+
     },
   },
   wo => {
-    xs_call => 'setters',
-    pp_code => sub {
-      my $set = "set_$_[0]";
-      my $field = $_[1];
-      $field =~ s/'/\\'/g;
-
-      "
-        \@_ != 1
-          ? shift->$set('$field', \@_)
-          : do {
-            my \$caller = caller;
-            my \$class = ref \$_[0] || \$_[0];
-            Carp::croak(\"'\$caller' cannot access the value of '$field' \".
-                        \"(write-only attributes of class '\$class')\");
-          }
-      "
+    cxsa_call => 'setters',
+    pp_generator => sub {
+      # my ($group, $fieldname) = @_;
+      my $quoted_fieldname = B::perlstring($_[1]);
+      sprintf  <<'EOS', $_[0], $quoted_fieldname;
+
+@_ > 1
+  ? shift->set_%s(%s, @_)
+  : do {
+    my ($meth) = (caller(0))[3] =~ /([^\:]+)$/;
+    my $class = length( ref($_[0]) ) ? ref($_[0]) : $_[0];
+    Carp::croak(
+      "'$meth' cannot access its value (write-only attribute of class $class)"
+    );
+  }
+EOS
+
     },
   },
 };
 
+my $cag_eval = sub {
+  #my ($src, $no_warnings, $err_msg) = @_;
+
+  my $src = sprintf "{ %s warnings; use strict; no strict 'refs'; %s }",
+    $_[1] ? 'no' : 'use',
+    $_[0],
+  ;
+
+  my (@rv, $err);
+  {
+    local $@ if __CAG_ENV__::UNSTABLE_DOLLARAT;
+    wantarray
+      ? @rv = eval $src
+      : $rv[0] = eval $src
+    ;
+    $err = $@ if $@ ne '';
+  }
+
+  Carp::croak(join ': ', ($_[2] || 'String-eval failed'), "$err\n$src\n" )
+    if defined $err;
+
+  wantarray ? @rv : $rv[0];
+};
 
 my ($accessor_maker_cache, $no_xsa_warned_classes);
 
@@ -633,12 +786,20 @@ my ($accessor_maker_cache, $no_xsa_warned_classes);
 my $original_simple_getter = __PACKAGE__->can ('get_simple');
 my $original_simple_setter = __PACKAGE__->can ('set_simple');
 
+my ($resolved_methods, $cag_produced_crefs);
+
+sub CLONE {
+  my @crefs = grep { defined $_ } values %{$cag_produced_crefs||{}};
+  $cag_produced_crefs = @crefs
+    ? { map { $_ => $_ } @crefs }
+    : undef
+  ;
+}
+
 # Note!!! Unusual signature
 $gen_accessor = sub {
   my ($type, $class, $group, $field, $methname) = @_;
-  if (my $c = Scalar::Util::blessed( $class )) {
-    $class = $c;
-  }
+  $class = ref $class if length ref $class;
 
   # When installing an XSA simple accessor, we need to make sure we are not
   # short-circuiting a (compile or runtime) get_simple/set_simple override.
@@ -653,14 +814,10 @@ $gen_accessor = sub {
     die sprintf( "Class::XSAccessor requested but not available:\n%s\n", __CAG_ENV__::NO_CXSA )
       if __CAG_ENV__::NO_CXSA;
 
-    my ($expected_cref, $cached_implementation);
-    my $ret = $expected_cref = sub {
-      my $current_class = Scalar::Util::blessed( $_[0] ) || $_[0];
+    my $ret = sub {
+      my $current_class = length (ref ($_[0] ) ) ? ref ($_[0]) : $_[0];
 
-      # $cached_implementation will be set only if the shim got
-      # 'around'ed, in which case it is handy to avoid re-running
-      # this block over and over again
-      my $resolved_implementation = $cached_implementation->{$current_class} || do {
+      my $resolved_implementation = $resolved_methods->{$current_class}{$methname} ||= do {
         if (
           ($current_class->can('get_simple')||0) == $original_simple_getter
             &&
@@ -679,7 +836,7 @@ $gen_accessor = sub {
           Class::XSAccessor->import(
             replace => 1,
             class => '__CAG__XSA__BREEDER__',
-            $maker_templates->{$type}{xs_call} => {
+            $maker_templates->{$type}{cxsa_call} => {
               $methname => $field,
             },
           );
@@ -706,11 +863,7 @@ $gen_accessor = sub {
       # if after this shim was created someone wrapped it with an 'around',
       # we can not blindly reinstall the method slot - we will destroy the
       # wrapper. Silently chain execution further...
-      if ( !$expected_cref or $expected_cref != ($current_class->can($methname)||0) ) {
-
-        # there is no point in re-determining it on every subsequent call,
-        # just store for future reference
-        $cached_implementation->{$current_class} ||= $resolved_implementation;
+      if ( ! $cag_produced_crefs->{ $current_class->can($methname) || 0 } ) {
 
         # older perls segfault if the cref behind the goto throws
         # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
@@ -719,18 +872,20 @@ $gen_accessor = sub {
         goto $resolved_implementation;
       }
 
+
       if (__CAG_ENV__::TRACK_UNDEFER_FAIL) {
         my $deferred_calls_seen = do {
           no strict 'refs';
           \%{"${current_class}::__cag_deferred_xs_shim_invocations"}
         };
         my @cframe = caller(0);
+
         if (my $already_seen = $deferred_calls_seen->{$cframe[3]}) {
           Carp::carp (
             "Deferred version of method $cframe[3] invoked more than once (originally "
           . "invoked at $already_seen). This is a strong indication your code has "
           . 'cached the original ->can derived method coderef, and is using it instead '
-          . 'of the proper method re-lookup, causing performance regressions'
+          . 'of the proper method re-lookup, causing minor performance regressions'
           );
         }
         else {
@@ -747,13 +902,16 @@ $gen_accessor = sub {
 
         my $fq_name = "${current_class}::${methname}";
         *$fq_name = Sub::Name::subname($fq_name, $resolved_implementation);
-
-        # need to update what the shim expects too *in case* its
-        # ->can was cached for some moronic reason
-        $expected_cref = $resolved_implementation;
-        Scalar::Util::weaken($expected_cref);
       }
 
+      # now things are installed - one ref less to carry
+      delete $resolved_methods->{$current_class}{$methname};
+
+      # but need to record it in the expectation registry *in case* it
+      # was cached via ->can for some moronic reason
+      Scalar::Util::weaken( $cag_produced_crefs->{$resolved_implementation} = $resolved_implementation );
+
+
       # older perls segfault if the cref behind the goto throws
       # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
       return $resolved_implementation->(@_) if __CAG_ENV__::BROKEN_GOTO;
@@ -761,18 +919,19 @@ $gen_accessor = sub {
       goto $resolved_implementation;
     };
 
-    Scalar::Util::weaken($expected_cref); # to break the self-reference
-    $ret;
+    Scalar::Util::weaken($cag_produced_crefs->{$ret} = $ret);
+
+    $ret; # returning shim
   }
 
   # no Sub::Name - just install the coderefs directly (compiling every time)
   elsif (__CAG_ENV__::NO_SUBNAME) {
     my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
-      $maker_templates->{$type}{pp_code}->($group, $field);
+      $maker_templates->{$type}{pp_generator}->($group, $field);
 
-    no warnings 'redefine';
-    local $@ if __CAG_ENV__::UNSTABLE_DOLLARAT;
-    eval "sub ${class}::${methname} { $src }";
+    $cag_eval->(
+      "no warnings 'redefine'; sub ${class}::${methname} { $src }; 1",
+    );
 
     undef;  # so that no further attempt will be made to install anything
   }
@@ -781,10 +940,9 @@ $gen_accessor = sub {
   else {
     ($accessor_maker_cache->{pp}{$type}{$group}{$field} ||= do {
       my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
-        $maker_templates->{$type}{pp_code}->($group, $field);
+        $maker_templates->{$type}{pp_generator}->($group, $field);
 
-      local $@ if __CAG_ENV__::UNSTABLE_DOLLARAT;
-      eval "sub { my \$dummy; sub { \$dummy if 0; $src } }" or die $@;
+      $cag_eval->( "sub { my \$dummy; sub { \$dummy if 0; $src } }" );
     })->()
   }
 };