use compiled constraints instead of objects origin/topics/use-compiled-constraints
Robert 'phaylon' Sedlacek [Sun, 24 Oct 2010 18:01:11 +0000 (20:01 +0200)]
lib/MooseX/Meta/TypeConstraint/Structured.pm
lib/MooseX/Types/Structured.pm
t/01-basic.t

index 6f4a6fe..0db946f 100644 (file)
@@ -109,12 +109,7 @@ of values (to be passed at check time)
 
 sub generate_constraint_for {
     my ($self, $type_constraints) = @_;
-    return sub {
-        my $arg =  shift @_;
-        my $constraint_generator = $self->constraint_generator;
-        my $result = $constraint_generator->($type_constraints, $arg, $_[0]);
-        return $result;
-    };
+    return $self->constraint_generator->($self, $type_constraints);
 }
 
 =method parameterize (@type_constraints)
index 2ec875a..3d9fb76 100644 (file)
@@ -719,6 +719,25 @@ my $Optional = MooseX::Meta::TypeConstraint::Structured::Optional->new(
     }
 );
 
+my $IsType = sub {
+    my ($obj, $type) = @_;
+
+    return $obj->can('equals')
+        ? $obj->equals($type)
+        : undef;
+};
+
+my $CompiledTC = sub {
+    my ($obj) = @_;
+
+    my $method = '_compiled_type_constraint';
+    return(
+          $obj->$IsType('Any')  ? undef
+        : $obj->can($method)    ? $obj->$method
+        :                         sub { $obj->check(shift) },
+    );
+};
+
 Moose::Util::TypeConstraints::register_type_constraint($Optional);
 Moose::Util::TypeConstraints::add_parameterizable_type($Optional);
 
@@ -728,7 +747,8 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
         parent => find_type_constraint('ArrayRef'),
         constraint_generator=> sub {
             ## Get the constraints and values to check
-            my ($type_constraints, $values) = @_;
+            my ($self, $type_constraints) = @_;
+            $type_constraints ||= $self->type_constraints;
             my @type_constraints = defined $type_constraints ?
              @$type_constraints : ();
 
@@ -738,50 +758,66 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
                 $overflow_handler = pop @type_constraints;
             }
 
-            my @values = defined $values ? @$values: ();
-            ## Perform the checking
-            while(@type_constraints) {
-                my $type_constraint = shift @type_constraints;
-                if(@values) {
-                    my $value = shift @values;
-                    unless($type_constraint->check($value)) {
-                        if($_[2]) {
-                           my $message = $type_constraint->validate($value,$_[2]);
-                           $_[2]->add_message({message=>$message,level=>$_[2]->level});
+            my (@checks, @optional, $o_check, $is_compiled);
+            return sub {
+                my ($values, $err) = @_;
+                my @values = defined $values ? @$values : ();
+
+                ## initialise on first time run
+                unless ($is_compiled) {
+                    @checks   = map { $_->$CompiledTC } @type_constraints;
+                    @optional = map { $_->is_subtype_of($Optional) } @type_constraints;
+                    $o_check  = $overflow_handler->$CompiledTC
+                        if $overflow_handler;
+                    $is_compiled++;
+                }
+
+                ## Perform the checking
+              VALUE:
+                for my $type_index (0 .. $#checks) {
+
+                    my $type_constraint = $checks[ $type_index ];
+
+                    if(@values) {
+                        my $value = shift @values;
+
+                        next VALUE
+                            unless $type_constraint;
+
+                        unless($type_constraint->($value)) {
+                            if($err) {
+                               my $message = $type_constraints[ $type_index ]->validate($value,$err);
+                               $err->add_message({message=>$message,level=>$err->level});
+                            }
+                            return;
+                        }
+                    } else {
+                        ## Test if the TC supports null values
+                        unless ($optional[ $type_index ]) {
+                            if($err) {
+                               my $message = $type_constraints[ $type_index ]->get_message('NULL',$err);
+                               $err->add_message({message=>$message,level=>$err->level});
+                            }
+                            return;
                         }
-                        return;
                     }
-                } else {
-                    ## Test if the TC supports null values
-                    unless ($type_constraint->is_subtype_of($Optional)) {
-                        if($_[2]) {
-                           my $message = $type_constraint->get_message('NULL',$_[2]);
-                           $_[2]->add_message({message=>$message,level=>$_[2]->level});
+                }
+
+                ## Make sure there are no leftovers.
+                if(@values) {
+                    if($overflow_handler) {
+                        return $o_check->([@values], $err);
+                    } else {
+                        if($err) {
+                            my $message = "More values than Type Constraints!";
+                            $err->add_message({message=>$message,level=>$err->level});
                         }
                         return;
                     }
-                }
-            }
-            ## Make sure there are no leftovers.
-            if(@values) {
-                if($overflow_handler) {
-                    return $overflow_handler->check([@values], $_[2]);
                 } else {
-                    if($_[2]) {
-                        my $message = "More values than Type Constraints!";
-                        $_[2]->add_message({message=>$message,level=>$_[2]->level});
-                    }
-                    return;
-                }
-            } elsif(@type_constraints) {
-                if($_[2]) {
-                    my $message = "Not enough values for all defined type constraints.  Remaining: ". join(', ',@type_constraints);
-                    $_[2]->add_message({message=>$message,level=>$_[2]->level});
+                    return 1;
                 }
-                return;
-            } else {
-                return 1;
-            }
+            };
         }
     )
 );
@@ -792,7 +828,8 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
         parent => find_type_constraint('HashRef'),
         constraint_generator => sub {
             ## Get the constraints and values to check
-            my ($type_constraints, $values) = @_;
+            my ($self, $type_constraints) = @_;
+            $type_constraints = $self->type_constraints;
             my @type_constraints = defined $type_constraints ?
              @$type_constraints : ();
 
@@ -802,51 +839,65 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
                 $overflow_handler = pop @type_constraints;
             }
             my (%type_constraints) = @type_constraints;
-            my %values = defined $values ? %$values: ();
-            ## Perform the checking
-            while(%type_constraints) {
-                my($key, $type_constraint) = each %type_constraints;
-                delete $type_constraints{$key};
-                if(exists $values{$key}) {
-                    my $value = $values{$key};
-                    delete $values{$key};
-                    unless($type_constraint->check($value)) {
-                        if($_[2]) {
-                            my $message = $type_constraint->validate($value,$_[2]);
-                            $_[2]->add_message({message=>$message,level=>$_[2]->level});
+
+            my (%check, %optional, $o_check, $is_compiled);
+            return sub {
+                my ($values, $err) = @_;
+                my %values = defined $values ? %$values: ();
+
+                unless ($is_compiled) {
+                    %check    = map { ($_ => $type_constraints{ $_ }->$CompiledTC) } keys %type_constraints;
+                    %optional = map { ($_ => $type_constraints{ $_ }->is_subtype_of($Optional)) } keys %type_constraints;
+                    $o_check  = $overflow_handler->$CompiledTC
+                        if $overflow_handler;
+                    $is_compiled++;
+                }
+
+                ## Perform the checking
+              KEY:
+                for my $key (keys %check) {
+                    my $type_constraint = $check{ $key };
+
+                    if(exists $values{$key}) {
+                        my $value = $values{$key};
+                        delete $values{$key};
+
+                        next KEY
+                            unless $type_constraint;
+
+                        unless($type_constraint->($value)) {
+                            if($err) {
+                                my $message = $type_constraints{ $key }->validate($value,$err);
+                                $err->add_message({message=>$message,level=>$err->level});
+                            }
+                            return;
+                        }
+                    } else {
+                        ## Test to see if the TC supports null values
+                        unless ($optional{ $key }) {
+                            if($err) {
+                               my $message = $type_constraints{ $key }->get_message('NULL',$err);
+                               $err->add_message({message=>$message,level=>$err->level});
+                            }
+                            return;
                         }
-                        return;
                     }
-                } else {
-                    ## Test to see if the TC supports null values
-                    unless ($type_constraint->is_subtype_of($Optional)) {
-                        if($_[2]) {
-                           my $message = $type_constraint->get_message('NULL',$_[2]);
-                           $_[2]->add_message({message=>$message,level=>$_[2]->level});
+                }
+
+                ## Make sure there are no leftovers.
+                if(%values) {
+                    if($overflow_handler) {
+                        return $o_check->(+{%values});
+                    } else {
+                        if($err) {
+                            my $message = "More values than Type Constraints!";
+                            $err->add_message({message=>$message,level=>$err->level});
                         }
                         return;
                     }
-                }
-            }
-            ## Make sure there are no leftovers.
-            if(%values) {
-                if($overflow_handler) {
-                    return $overflow_handler->check(+{%values});
                 } else {
-                    if($_[2]) {
-                        my $message = "More values than Type Constraints!";
-                        $_[2]->add_message({message=>$message,level=>$_[2]->level});
-                    }
-                    return;
-                }
-            } elsif(%type_constraints) {
-                if($_[2]) {
-                    my $message = "Not enough values for all defined type constraints.  Remaining: ". join(', ',@type_constraints);
-                    $_[2]->add_message({message=>$message,level=>$_[2]->level});
+                    return 1;
                 }
-                return;
-            } else {
-                return 1;
             }
         },
     )
@@ -858,7 +909,8 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
     parent => find_type_constraint('HashRef'),
     constraint_generator=> sub {
       ## Get the constraints and values to check
-      my ($type_constraints, $values) = @_;
+      my ($self, $type_constraints) = @_;
+      $type_constraints = $self->type_constraints;
       my @constraints = defined $type_constraints ? @$type_constraints : ();
 
       Carp::confess( "too many args for Map type" ) if @constraints > 2;
@@ -867,33 +919,44 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
                                   : @constraints == 1 ? (undef, @constraints)
                                   :                     ();
 
-      my %values = defined $values ? %$values: ();
-      ## Perform the checking
-      if ($value_type) {
-        for my $value (values %$values) {
-          unless ($value_type->check($value)) {
-            if($_[2]) {
-              my $message = $value_type->validate($value,$_[2]);
-              $_[2]->add_message({message=>$message,level=>$_[2]->level});
+      my ($key_check, $value_check, $is_compiled);
+      return sub {
+          my ($values, $err) = @_;
+          my %values = defined $values ? %$values: ();
+
+          unless ($is_compiled) {
+              ($key_check, $value_check)
+                = map { $_ ? $_->$CompiledTC : undef }
+                      $key_type, $value_type;
+              $is_compiled++;
+          }
+
+          ## Perform the checking
+          if ($value_check) {
+            for my $value (values %$values) {
+              unless ($value_check->($value)) {
+                if($err) {
+                  my $message = $value_type->validate($value,$err);
+                  $err->add_message({message=>$message,level=>$err->level});
+                }
+                return;
+              }
             }
-            return;
           }
-        }
-      }
-
-      if ($key_type) {
-        for my $key (keys %$values) {
-          unless ($key_type->check($key)) {
-            if($_[2]) {
-              my $message = $key_type->validate($key,$_[2]);
-              $_[2]->add_message({message=>$message,level=>$_[2]->level});
+          if ($key_check) {
+            for my $key (keys %$values) {
+              unless ($key_check->($key)) {
+                if($err) {
+                  my $message = $key_type->validate($key,$err);
+                  $err->add_message({message=>$message,level=>$err->level});
+                }
+                return;
+              }
             }
-            return;
           }
-        }
-      }
 
-      return 1;
+          return 1;
+      };
     },
   )
 );
index 58b24cb..184b82f 100644 (file)
@@ -15,17 +15,22 @@ my $list_tc = MooseX::Meta::TypeConstraint::Structured->new(
     parent => $arrayref,
     type_constraints => [$int, $str],
     constraint_generator=> sub {
-        my @type_constraints = @{shift @_};
-        my @values = @{shift @_};
-
-        while(my $type_constraint = shift @type_constraints) {
-            my $value = shift @values || return;
-            $type_constraint->check($value) || return;
-        }
-        if(@values) {
-            return;
-        } else {
-            return 1;
+        my ($self) = @_;
+        my @type_constraints = @{ $self->type_constraints };
+
+        return sub {
+            my ($values, $err) = @_;
+            my @values = @$values;
+
+            for my $type_constraint (@type_constraints) {
+                my $value = shift @values || return;
+                $type_constraint->check($value) || return;
+            }
+            if(@values) {
+                return;
+            } else {
+                return 1;
+            }
         }
     }
 );