use XSAccessor if available
Matt S Trout [Mon, 8 Nov 2010 05:49:02 +0000 (05:49 +0000)]
lib/Class/Tiny/_Utils.pm
lib/Method/Generate/Accessor.pm
t/method-generate-accessor.t

index b046068..b166515 100644 (file)
@@ -3,7 +3,7 @@ package Class::Tiny::_Utils;
 use strictures 1;
 use base qw(Exporter);
 
-our @EXPORT = qw(_getglob _install_modifier);
+our @EXPORT = qw(_getglob _install_modifier _maybe_load_module);
 
 sub _getglob { no strict 'refs'; \*{$_[0]} }
 
@@ -22,4 +22,20 @@ sub _install_modifier {
   Class::Method::Modifiers::install_modifier(@_);
 }
 
+our %MAYBE_LOADED;
+
+sub _maybe_load_module {
+  return $MAYBE_LOADED{$_[0]} if exists $MAYBE_LOADED{$_[0]};
+  (my $proto = $_[0]) =~ s/::/\//g;
+  if (eval { require "${proto}.pm"; 1 }) {
+    $MAYBE_LOADED{$_[0]} = 1;
+  } else {
+    if (exists $INC{"${proto}.pm"}) {
+      warn "$_[0] exists but failed to load with error: $@";
+    }
+    $MAYBE_LOADED{$_[0]} = 0;
+  }
+  return $MAYBE_LOADED{$_[0]};
+}
+
 1;
index 263c7f8..581386f 100644 (file)
@@ -5,11 +5,17 @@ use Class::Tiny::_Utils;
 use base qw(Class::Tiny::Object);
 use Sub::Quote;
 use B 'perlstring';
+BEGIN {
+  our $CAN_HAZ_XS = ($^O ne 'Win32')
+    && _maybe_load_module('Class::XSAccessor')
+    && (Class::XSAccessor->VERSION > 1.06);
+}
 
 sub generate_method {
   my ($self, $into, $name, $spec, $quote_opts) = @_;
   die "Must have an is" unless my $is = $spec->{is};
   local $self->{captures} = {};
+  local $self->{into} = $into; # for XS gen
   my $body = do {
     if ($is eq 'ro') {
       $self->_generate_get($name, $spec)
@@ -29,6 +35,7 @@ sub generate_method {
       "    delete \$_[0]->{${\perlstring $name}}\n"
     ;
   }
+  return $body if ref($body); # optimiferised
   quote_sub
     "${into}::${name}" => '    '.$body."\n",
     $self->{captures}, $quote_opts||{}
@@ -43,8 +50,21 @@ sub is_simple_attribute {
     qw(lazy default builder isa trigger predicate);
 }
 
+sub is_simple_get {
+  my ($self, $name, $spec) = @_;
+  return !($spec->{lazy} and ($spec->{default} or $spec->{builder}));
+}
+
+sub is_simple_set {
+  my ($self, $name, $spec) = @_;
+  return !grep $spec->{$_}, qw(isa trigger);
+}
+
 sub _generate_get {
   my ($self, $name, $spec) = @_;
+  if (our $CAN_HAZ_XS && $self->is_simple_get($name, $spec)) {
+    return $self->_generate_xs_get($name);
+  }
   my $simple = $self->_generate_simple_get('$_[0]', $name);
   my ($lazy, $default, $builder) = @{$spec}{qw(lazy default builder)};
   return $simple unless $lazy and ($default or $builder);
@@ -231,8 +251,33 @@ sub _generate_simple_set {
 
 sub _generate_getset {
   my ($self, $name, $spec) = @_;
+  if (
+    our $CAN_HAZ_XS
+    && $self->is_simple_get($name, $spec)
+    && $self->is_simple_set($name, $spec)
+  ) {
+    return $self->_generate_xs_getset($name);
+  }
   q{(@_ > 1 ? }.$self->_generate_set($name, q{$_[1]}, $spec)
     .' : '.$self->_generate_get($name).')';
 }
 
+sub _generate_xs_get {
+  shift->_generate_xs('getters', @_);
+}
+
+sub _generate_xs_getset {
+  shift->_generate_xs('accessors', @_);
+}
+
+sub _generate_xs {
+  my ($self, $type, $name) = @_;
+  no strict 'refs';
+  Class::XSAccessor->import(
+    class => $self->{into},
+    $type => { $name => $name }
+  );
+  return $self->{into}->can($name);
+}
+
 1;
index 4c15a94..93164c8 100644 (file)
@@ -28,7 +28,7 @@ like(
 my $foo = Foo->new(one => 1);
 
 is($foo->one, 1, 'ro reads');
-$foo->one(-3);
+$foo->one(-3) unless $Method::Generate::Accessor::CAN_HAZ_XS;
 is($foo->one, 1, 'ro does not write');
 
 is($foo->two, undef, 'rw reads');