Merge remote branch 'origin/master' into merge-0.74
Fuji, Goro [Mon, 27 Sep 2010 05:39:08 +0000 (14:39 +0900)]
Conflicts:
lib/Mouse/Meta/Method/Constructor.pm

16 files changed:
author/attr_order.pl [new file with mode: 0644]
lib/Mouse/Meta/Attribute.pm
lib/Mouse/Meta/Class.pm
lib/Mouse/Meta/Method/Accessor.pm
lib/Mouse/Meta/Method/Constructor.pm
lib/Mouse/Meta/Module.pm
lib/Mouse/Meta/TypeConstraint.pm
lib/Mouse/PurePerl.pm
lib/Mouse/Role.pm
lib/Mouse/Util.pm
lib/Test/Mouse.pm
t/001_mouse/007-attributes.t
t/001_mouse/028-subclass-attr.t
xs-src/Mouse.xs
xs-src/MouseAttribute.xs
xs-src/MouseTypeConstraints.xs

diff --git a/author/attr_order.pl b/author/attr_order.pl
new file mode 100644 (file)
index 0000000..14c688b
--- /dev/null
@@ -0,0 +1,31 @@
+package Base;
+use Any::Moose;
+
+has [qw(aaa bbb ccc)] => (
+    is => 'rw',
+);
+
+package D1;
+use Any::Moose;
+extends qw(Base);
+has [qw(ddd eee fff)] => (
+    is => 'rw',
+);
+
+package D2;
+use Any::Moose;
+extends qw(D1);
+has [qw(ggg hhh iii)] => (
+    is => 'rw',
+);
+
+package main;
+use Test::More;
+use Test::Mouse;
+
+with_immutable {
+    my $attrs_list = join ",",
+        map { $_->name } D2->meta->get_all_attributes;
+    is $attrs_list, join ",", qw(aaa bbb ccc ddd eee fff ggg hhh iii);
+} qw(Base D1 D2);
+done_testing;
index d38af3b..3a32b74 100644 (file)
@@ -93,13 +93,13 @@ sub get_write_method  { $_[0]->writer || $_[0]->accessor }
 
 sub get_read_method_ref{
     my($self) = @_;
-    return $self->{_read_method_ref}
+    return $self->{_mouse_cache_read_method_ref}
         ||= $self->_get_accessor_method_ref('get_read_method', '_generate_reader');
 }
 
 sub get_write_method_ref{
     my($self) = @_;
-    return $self->{_write_method_ref}
+    return $self->{_mouse_cache_write_method_ref}
         ||= $self->_get_accessor_method_ref('get_write_method', '_generate_writer');
 }
 
@@ -182,7 +182,7 @@ sub clone_and_inherit_options{
 
     # remove temporary caches
     foreach my $attr(keys %{$args}){
-        if($attr =~ /\A _/xms){
+        if($attr =~ /\A _mouse_cache_/xms){
             delete $args->{$attr};
         }
     }
@@ -223,7 +223,7 @@ sub get_value {
 
 sub has_value {
     my($self, $object) = @_;
-    my $accessor_ref = $self->{_predicate_ref}
+    my $accessor_ref = $self->{_mouse_cache_predicate_ref}
         ||= $self->_get_accessor_method_ref('predicate', '_generate_predicate');
 
     return $accessor_ref->($object);
@@ -231,7 +231,7 @@ sub has_value {
 
 sub clear_value {
     my($self, $object) = @_;
-    my $accessor_ref = $self->{_crealer_ref}
+    my $accessor_ref = $self->{_mouse_cache_crealer_ref}
         ||= $self->_get_accessor_method_ref('clearer', '_generate_clearer');
 
     return $accessor_ref->($object);
index d4d477c..462029a 100644 (file)
@@ -1,7 +1,7 @@
 package Mouse::Meta::Class;
 use Mouse::Util qw/:meta/; # enables strict and warnings
 
-use Scalar::Util qw/blessed weaken/;
+use Scalar::Util ();
 
 use Mouse::Meta::Module;
 our @ISA = qw(Mouse::Meta::Module);
@@ -154,7 +154,7 @@ sub _collect_roles {
 }
 
 
-sub find_method_by_name{
+sub find_method_by_name {
     my($self, $method_name) = @_;
     defined($method_name)
         or $self->throw_error('You must define a method name to find');
@@ -179,14 +179,14 @@ sub get_all_method_names {
             $self->linearized_isa;
 }
 
-sub find_attribute_by_name{
+sub find_attribute_by_name {
     my($self, $name) = @_;
-    my $attr;
-    foreach my $class($self->linearized_isa){
-        my $meta = Mouse::Util::get_metaclass_by_name($class) or next;
-        $attr = $meta->get_attribute($name) and last;
+    defined($name)
+        or $self->throw_error('You must define an attribute name to find');
+    foreach my $attr($self->get_all_attributes) {
+        return $attr if $attr->name eq $name;
     }
-    return $attr;
+    return undef;
 }
 
 sub add_attribute {
@@ -194,7 +194,7 @@ sub add_attribute {
 
     my($attr, $name);
 
-    if(blessed $_[0]){
+    if(Scalar::Util::blessed($_[0])){
         $attr = $_[0];
 
         $attr->isa('Mouse::Meta::Attribute')
@@ -225,26 +225,36 @@ sub add_attribute {
         }
     }
 
-    weaken( $attr->{associated_class} = $self );
+    Scalar::Util::weaken( $attr->{associated_class} = $self );
 
     # install accessors first
     $attr->install_accessors();
 
     # then register the attribute to the metaclass
-    $attr->{insertion_order} = keys %{ $self->{attributes} };
-    $self->{attributes}{$attr->name} = $attr;
+    $attr->{insertion_order}   = keys %{ $self->{attributes} };
+    $self->{attributes}{$name} = $attr;
+    delete $self->{_mouse_cache}; # clears internal cache
 
     if(!$attr->{associated_methods} && ($attr->{is} || '') ne 'bare'){
         Carp::carp(qq{Attribute ($name) of class }.$self->name
             .qq{ has no associated methods (did you mean to provide an "is" argument?)});
     }
+    return $attr;
+}
 
-    if(!Mouse::Util::MOUSE_XS) {
-        # in Mouse::PurePerl, attribute initialization code is cached, so it
-        # must be clear here. See _initialize_object() in Mouse::PurePerl.
-        delete $self->{_initialize_object};
+sub _calculate_all_attributes {
+    my($self) = @_;
+    my %seen;
+    my @all_attrs;
+    foreach my $class($self->linearized_isa) {
+        my $meta  = Mouse::Util::get_metaclass_by_name($class) or next;
+        my @attrs = grep { !$seen{$_->name}++ } values %{$meta->{attributes}};
+        @attrs = sort {
+                $b->{insertion_order} <=> $a->{insertion_order}
+            } @attrs;
+        push @all_attrs, @attrs;
     }
-    return $attr;
+    return [reverse @all_attrs];
 }
 
 sub linearized_isa;
index 6b62da0..3f7b6b4 100644 (file)
@@ -97,7 +97,6 @@ sub _generate_accessor_any{
         }
         elsif(defined $constraint){
             $accessor .= "my \$tmp = $value;\n";
-
             $accessor .= "\$compiled_type_constraint->(\$tmp)";
             $accessor .= " || \$attribute->_throw_type_constraint_error(\$tmp, \$constraint);\n";
             $accessor .= "$slot = \$tmp;\n";
index e641611..c74d359 100644 (file)
@@ -15,9 +15,8 @@ sub _generate_constructor {
 
     my $buildall      = $class->_generate_BUILDALL($metaclass);
     my $buildargs     = $class->_generate_BUILDARGS($metaclass);
-    my $initializer   = $metaclass->{_initialize_object} ||= do {
+    my $initializer   = $metaclass->{_mouse_cache}{_initialize_object} ||=
        $class->_generate_initialize_object($metaclass);
-    };
     my $source = sprintf(<<'EOT', __FILE__, $metaclass->name, $buildargs, $buildall);
 #line 1 "%s"
         package %s;
index 5f355bb..d7439d8 100644 (file)
@@ -1,5 +1,5 @@
 package Mouse::Meta::Module;
-use Mouse::Util qw/:meta get_code_package get_code_ref not_supported/; # enables strict and warnings
+use Mouse::Util qw/:meta/; # enables strict and warnings
 
 use Carp         ();
 use Scalar::Util ();
@@ -87,7 +87,7 @@ my %foreign = map{ $_ => undef } qw(
 sub _code_is_mine{
 #    my($self, $code) = @_;
 
-    return !exists $foreign{ get_code_package($_[1]) };
+    return !exists $foreign{ Mouse::Util::get_code_package($_[1]) };
 }
 
 sub add_method;
@@ -99,7 +99,7 @@ sub has_method {
         or $self->throw_error('You must define a method name');
 
     return defined($self->{methods}{$method_name}) || do{
-        my $code = get_code_ref($self->{package}, $method_name);
+        my $code = Mouse::Util::get_code_ref($self->{package}, $method_name);
         $code && $self->_code_is_mine($code);
     };
 }
@@ -111,7 +111,7 @@ sub get_method_body {
         or $self->throw_error('You must define a method name');
 
     return $self->{methods}{$method_name} ||= do{
-        my $code = get_code_ref($self->{package}, $method_name);
+        my $code = Mouse::Util::get_code_ref($self->{package}, $method_name);
         $code && $self->_code_is_mine($code) ? $code : undef;
     };
 }
index 6bb7e21..2dcba43 100644 (file)
@@ -1,6 +1,5 @@
 package Mouse::Meta::TypeConstraint;
 use Mouse::Util qw(:meta); # enables strict and warnings
-use Scalar::Util ();
 
 sub new {
     my $class = shift;
@@ -8,7 +7,7 @@ sub new {
 
     $args{name} = '__ANON__' if !defined $args{name};
 
-    if($args{parent}) {
+    if(defined $args{parent}) {
         %args = (%{$args{parent}}, %args);
         # a child type must not inherit 'compiled_type_constraint'
         # and 'hand_optimized_type_constraint' from the parent
@@ -72,6 +71,11 @@ sub compile_type_constraint;
 sub _add_type_coercions { # ($self, @pairs)
     my $self = shift;
 
+    if(exists $self->{type_constraints}){ # union type
+        $self->throw_error(
+            "Cannot add additional type coercions to Union types '$self'");
+    }
+
     my $coercions = ($self->{coercion_map} ||= []);
     my %has       = map{ $_->[0] => undef } @{$coercions};
 
@@ -90,14 +94,7 @@ sub _add_type_coercions { # ($self, @pairs)
         push @{$coercions}, [ $type => $action ];
     }
 
-    # compile
-    if(exists $self->{type_constraints}){ # union type
-        $self->throw_error(
-            "Cannot add additional type coercions to Union types");
-    }
-    else{
-        $self->_compile_type_coercion();
-    }
+    $self->_compile_type_coercion();
     return;
 }
 
@@ -144,14 +141,10 @@ sub _compile_union_type_coercion {
 
 sub coerce {
     my $self = shift;
-
-    my $coercion = $self->_compiled_type_coercion;
-    if(!$coercion){
-        $self->throw_error("Cannot coerce without a type coercion");
-    }
-
     return $_[0] if $self->check(@_);
 
+    my $coercion = $self->{_compiled_type_coercion}
+        or $self->throw_error("Cannot coerce without a type coercion");
     return  $coercion->(@_);
 }
 
@@ -172,7 +165,7 @@ sub get_message {
     }
 }
 
-sub is_a_type_of{
+sub is_a_type_of {
     my($self, $other) = @_;
 
     # ->is_a_type_of('__ANON__') is always false
@@ -222,7 +215,7 @@ sub assert_valid {
 }
 
 sub _as_string { $_[0]->name                  } # overload ""
-sub _identity  { Scalar::Util::refaddr($_[0]) } # overload 0+
+sub _identity;                                  # overload 0+
 
 sub _unite { # overload infix:<|>
     my($lhs, $rhs) = @_;
index a91ef4f..5f2527f 100644 (file)
@@ -134,7 +134,7 @@ sub generate_can_predicate_for {
 
 package Mouse::Util::TypeConstraints;
 
-use Scalar::Util qw(blessed looks_like_number openhandle);
+use Scalar::Util ();
 
 sub Any        { 1 }
 sub Item       { 1 }
@@ -143,15 +143,18 @@ sub Bool       { $_[0] ? $_[0] eq '1' : 1 }
 sub Undef      { !defined($_[0]) }
 sub Defined    {  defined($_[0])  }
 sub Value      {  defined($_[0]) && !ref($_[0]) }
-sub Num        {  looks_like_number($_[0]) }
-sub Int        {
-    my($value) = @_;
-    looks_like_number($value) && $value =~ /\A [+-]? [0-9]+  \z/xms;
-}
+sub Num        {  Scalar::Util::looks_like_number($_[0]) }
 sub Str        {
+    # We need to use a copy here to flatten MAGICs, for instance as in
+    # Str( substr($_, 0, 42) ).
     my($value) = @_;
     return defined($value) && ref(\$value) eq 'SCALAR';
 }
+sub Int        {
+    # We need to use a copy here to save the original internal SV flags.
+    my($value) = @_;
+    return defined($value) && $value =~ /\A -? [0-9]+  \z/xms;
+}
 
 sub Ref        { ref($_[0]) }
 sub ScalarRef  {
@@ -165,10 +168,12 @@ sub RegexpRef  { ref($_[0]) eq 'Regexp' }
 sub GlobRef    { ref($_[0]) eq 'GLOB'   }
 
 sub FileHandle {
-    return openhandle($_[0])  || (blessed($_[0]) && $_[0]->isa("IO::Handle"))
+    my($value) = @_;
+    return Scalar::Util::openhandle($value)
+        || (Scalar::Util::blessed($value) && $value->isa("IO::Handle"))
 }
 
-sub Object     { blessed($_[0]) && blessed($_[0]) ne 'Regexp' }
+sub Object     { Scalar::Util::blessed($_[0]) && ref($_[0]) ne 'Regexp' }
 
 sub ClassName  { Mouse::Util::is_class_loaded($_[0]) }
 sub RoleName   { (Mouse::Util::class_of($_[0]) || return 0)->isa('Mouse::Meta::Role') }
@@ -282,12 +287,6 @@ sub roles { $_[0]->{roles} }
 
 sub linearized_isa { @{ Mouse::Util::get_linear_isa($_[0]->{package}) } }
 
-sub get_all_attributes {
-    my($self) = @_;
-    my %attrs = map { %{ $self->initialize($_)->{attributes} } } reverse $self->linearized_isa;
-    return values %attrs;
-}
-
 sub new_object {
     my $meta = shift;
     my %args = (@_ == 1 ? %{$_[0]} : @_);
@@ -312,7 +311,7 @@ sub clone_object {
     my $object = shift;
     my $args   = $object->Mouse::Object::BUILDARGS(@_);
 
-    (blessed($object) && $object->isa($class->name))
+    (Scalar::Util::blessed($object) && $object->isa($class->name))
         || $class->throw_error("You must pass an instance of the metaclass (" . $class->name . "), not ($object)");
 
     my $cloned = bless { %$object }, ref $object;
@@ -324,13 +323,18 @@ sub _initialize_object{
     my($self, $object, $args, $is_cloning) = @_;
     # The initializer, which is used everywhere, must be clear
     # when an attribute is added. See Mouse::Meta::Class::add_attribute.
-    my $initializer = $self->{_initialize_object} ||= do {
+    my $initializer = $self->{_mouse_cache}{_initialize_object} ||=
         Mouse::Util::load_class($self->constructor_class)
             ->_generate_initialize_object($self);
-    };
     goto &{$initializer};
 }
 
+sub get_all_attributes {
+    my($self) = @_;
+    return @{ $self->{_mouse_cache}{all_attributes}
+        ||= $self->_calculate_all_attributes };
+}
+
 sub is_immutable {  $_[0]->{is_immutable} }
 
 sub strict_constructor;
@@ -600,6 +604,8 @@ sub name    { $_[0]->{name}    }
 sub parent  { $_[0]->{parent}  }
 sub message { $_[0]->{message} }
 
+sub _identity  { Scalar::Util::refaddr($_[0]) } # overload 0+
+
 sub type_parameter           { $_[0]->{type_parameter} }
 sub _compiled_type_constraint{ $_[0]->{compiled_type_constraint} }
 sub _compiled_type_coercion  { $_[0]->{_compiled_type_coercion}  }
index 1f89760..ea8c750 100644 (file)
@@ -6,8 +6,6 @@ our $VERSION = '0.74';
 use Carp         qw(confess);
 use Scalar::Util qw(blessed);
 
-use Mouse::Util  qw(not_supported);
-use Mouse::Meta::Role;
 use Mouse ();
 
 Mouse::Exporter->setup_import_methods(
@@ -104,7 +102,7 @@ sub requires {
 }
 
 sub excludes {
-    not_supported;
+    Mouse::Util::not_supported();
 }
 
 sub init_meta{
index b5a433f..656f642 100644 (file)
@@ -334,7 +334,7 @@ sub quoted_english_list {
 sub not_supported{
     my($feature) = @_;
 
-    $feature ||= ( caller(1) )[3]; # subroutine name
+    $feature ||= ( caller(1) )[3] . '()'; # subroutine name
 
     local $Carp::CarpLevel = $Carp::CarpLevel + 1;
     Carp::confess("Mouse does not currently support $feature");
index 6bf1ba7..ff1b98d 100644 (file)
@@ -69,9 +69,9 @@ sub with_immutable (&@) { ## no critic
     $block->();
     $_->meta->make_immutable for @_;
     $block->();
+    return if not defined wantarray;
 
     my $num_tests = $Test->current_test - $before;
-
     return !grep{ !$_ } ($Test->summary)[-$num_tests .. -1];
 }
 
@@ -114,13 +114,10 @@ does for the C<isa> method.
 Tests if a class or object has a certain attribute, similar to what C<can_ok>
 does for the methods.
 
-=back
+=item B<with_immutable { CODE } @class_names>
 
-=head1 SEE ALSO
-
-=over 4
-
-=item L<Test::More>
+Runs I<CODE> *which should contain normal tests) twice, and make each
+class in I<@class_names> immutable between the two runs.
 
 =back
 
@@ -130,5 +127,7 @@ L<Mouse>
 
 L<Test::Moose>
 
+L<Test::More>
+
 =cut
 
index f4c60ee..e4afacb 100644 (file)
@@ -30,54 +30,54 @@ do {
         writer   => 'write_attr',
     );
 };
-
-ok(!Class->can('x'), "No accessor is injected if 'is' has no value");
-can_ok('Class', 'y', 'z');
-
-has_attribute_ok 'Class', 'x';
-has_attribute_ok 'Class', 'y';
-has_attribute_ok 'Class', 'z';
-
-my $object = Class->new;
-
-ok(!$object->can('x'), "No accessor is injected if 'is' has no value");
-can_ok($object, 'y', 'z');
-
-is($object->y, undef);
-
-throws_ok {
-    $object->y(10);
-} qr/Cannot assign a value to a read-only accessor/;
-
-is($object->y, undef);
-
-is($object->z, undef);
-is($object->z(10), 10);
-is($object->z, 10);
-
-can_ok($object, qw(rw_attr read_attr write_attr));
-$object->write_attr(42);
-is $object->rw_attr, 42;
-is $object->read_attr, 42;
-$object->rw_attr(100);
-is $object->rw_attr, 100;
-is $object->read_attr, 100;
-
-is $object->write_attr("piyo"), "piyo";
-is $object->rw_attr("yopi"),    "yopi";
-
-dies_ok {
-    Class->rw_attr();
-};
-dies_ok {
-    Class->read_attr();
-};
-dies_ok {
-    Class->write_attr(42);
-};
-
-my @attrs = map { $_->name }
-    sort { $a->insertion_order <=> $b->insertion_order } $object->meta->get_all_attributes;
-is join(' ', @attrs), 'x y z attr', 'insertion_order';
-
+with_immutable {
+    ok(!Class->can('x'), "No accessor is injected if 'is' has no value");
+    can_ok('Class', 'y', 'z');
+
+    has_attribute_ok 'Class', 'x';
+    has_attribute_ok 'Class', 'y';
+    has_attribute_ok 'Class', 'z';
+
+    my $object = Class->new;
+
+    ok(!$object->can('x'), "No accessor is injected if 'is' has no value");
+    can_ok($object, 'y', 'z');
+
+    is($object->y, undef);
+
+    throws_ok {
+        $object->y(10);
+    } qr/Cannot assign a value to a read-only accessor/;
+
+    is($object->y, undef);
+
+    is($object->z, undef);
+    is($object->z(10), 10);
+    is($object->z, 10);
+
+    can_ok($object, qw(rw_attr read_attr write_attr));
+    $object->write_attr(42);
+    is $object->rw_attr, 42;
+    is $object->read_attr, 42;
+    $object->rw_attr(100);
+    is $object->rw_attr, 100;
+    is $object->read_attr, 100;
+
+    is $object->write_attr("piyo"), "piyo";
+    is $object->rw_attr("yopi"),    "yopi";
+
+    dies_ok {
+        Class->rw_attr();
+    };
+    dies_ok {
+        Class->read_attr();
+    };
+    dies_ok {
+        Class->write_attr(42);
+    };
+
+    my @attrs = map { $_->name }
+        sort { $a->insertion_order <=> $b->insertion_order } $object->meta->get_all_attributes;
+    is join(' ', @attrs), 'x y z attr', 'insertion_order';
+} qw(Class);
 done_testing;
index 9a4eaba..8abd69d 100644 (file)
@@ -1,8 +1,8 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use Test::More tests => 11;
-
+use Test::More;
+use Test::Mouse;
 do {
     package Class;
     use Mouse;
@@ -20,16 +20,37 @@ do {
         is  => 'rw',
         isa => 'Bool',
     );
-};
 
-my $obj = Child->new(class => 1, child => 1);
-ok($obj->child, "local attribute set in constructor");
-ok($obj->class, "inherited attribute set in constructor");
-
-is_deeply([sort(Child->meta->get_all_attributes)], [sort(
-    Child->meta->get_attribute('child'),
-    Class->meta->get_attribute('class'),
-)], "correct get_all_attributes");
+    package CA;
+    use Mouse;
+    extends qw(Class);
+    has ca => (is => 'rw');
+    package CB;
+    use Mouse;
+    extends qw(Class);
+    has cb => (is => 'rw');
+    package CC;
+    use Mouse;
+    extends qw(CB CA);
+    has cc => (is => 'rw');
+};
+with_immutable {
+    my $obj = Child->new(class => 1, child => 1);
+    ok($obj->child, "local attribute set in constructor");
+    ok($obj->class, "inherited attribute set in constructor");
+
+    is_deeply([sort(Child->meta->get_all_attributes)], [sort(
+        Child->meta->get_attribute('child'),
+        Class->meta->get_attribute('class'),
+    )], "correct get_all_attributes");
+
+    is_deeply([sort(CC->meta->get_all_attributes)], [sort(
+        CC->meta->get_attribute('cc'),
+        CB->meta->get_attribute('cb'),
+        CA->meta->get_attribute('ca'),
+        Class->meta->get_attribute('class'),
+    )], "correct get_all_attributes");
+} qw(Class CA CB CC);
 
 do {
     package Foo;
@@ -49,23 +70,27 @@ do {
     );
 };
 
-my $foo = Foo->new;
-is($foo->attr, 'Foo', 'subclass does not affect parent attr');
+with_immutable {
+    my $foo = Foo->new;
+    is($foo->attr, 'Foo', 'subclass does not affect parent attr');
+
+    my $bar = Bar->new;
+    is($bar->attr, undef, 'new attribute does not have the new default');
 
-my $bar = Bar->new;
-is($bar->attr, undef, 'new attribute does not have the new default');
+    is(Foo->meta->get_attribute('attr')->default, 'Foo');
+    is(Foo->meta->get_attribute('attr')->_is_metadata, 'ro');
 
-is(Foo->meta->get_attribute('attr')->default, 'Foo');
-is(Foo->meta->get_attribute('attr')->_is_metadata, 'ro');
+    is(Bar->meta->get_attribute('attr')->default, undef);
+    is(Bar->meta->get_attribute('attr')->_is_metadata, 'rw');
 
-is(Bar->meta->get_attribute('attr')->default, undef);
-is(Bar->meta->get_attribute('attr')->_is_metadata, 'rw');
+    is_deeply([Foo->meta->get_all_attributes], [
+        Foo->meta->get_attribute('attr'),
+    ], "correct get_all_attributes");
 
-is_deeply([Foo->meta->get_all_attributes], [
-    Foo->meta->get_attribute('attr'),
-], "correct get_all_attributes");
+    is_deeply([Bar->meta->get_all_attributes], [
+        Bar->meta->get_attribute('attr'),
+    ], "correct get_all_attributes");
+} qw(Foo Bar);
 
-is_deeply([Bar->meta->get_all_attributes], [
-    Bar->meta->get_attribute('attr'),
-], "correct get_all_attributes");
+done_testing;
 
index fd27c5d..b8951c1 100644 (file)
@@ -49,31 +49,13 @@ enum mouse_modifier_t {
 
 static MGVTBL mouse_xc_vtbl; /* for identity */
 
-static void
-mouse_class_push_attribute_list(pTHX_ SV* const metaclass, AV* const attrall, HV* const seen){
-    dSP;
-    I32 n;
-
-    /* $meta->get_attribute_list */
-    PUSHMARK(SP);
-    XPUSHs(metaclass);
-    PUTBACK;
-
-    n = call_sv(mouse_get_attribute_list, G_ARRAY | G_METHOD);
-    for(NOOP; n > 0; n--){
-        SV* name;
-
-        SPAGAIN;
-        name = POPs;
-        PUTBACK;
-
-        if(hv_exists_ent(seen, name, 0U)){
-            continue;
-        }
-        (void)hv_store_ent(seen, name, &PL_sv_undef, 0U);
-
-        av_push(attrall, newSVsv( mcall1(metaclass, mouse_get_attribute, name) ));
+static AV*
+mouse_calculate_all_attributes(pTHX_ SV* const metaclass) {
+    SV* const avref = mcall0s(metaclass, "_calculate_all_attributes");
+    if(!(SvROK(avref) && SvTYPE(SvRV(avref)) == SVt_PVAV)) {
+        croak("$meta->_calculate_all_attributes did not return an ARRAY reference");
     }
+    return (AV*)SvRV(avref);
 }
 
 XS(XS_Mouse__Object_BUILDARGS); /* prototype */
@@ -88,19 +70,16 @@ mouse_class_has_custom_buildargs(pTHX_ HV* const stash) {
 static void
 mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stash, AV* const xc) {
     AV* const linearized_isa = mro_get_linear_isa(stash);
-    I32 const len            = AvFILLp(linearized_isa);
+    I32 const len            = AvFILLp(linearized_isa) + 1;
     I32 i;
     U32 flags             = 0x00;
-    AV* const attrall     = newAV();
     AV* const buildall    = newAV();
     AV* const demolishall = newAV();
-    HV* const seen        = newHV(); /* for attributes */
+    AV* attrall;
 
     ENTER;
     SAVETMPS;
 
-    sv_2mortal((SV*)seen);
-
      /* old data will be delete at the end of the perl scope */
     av_delete(xc, MOUSE_XC_DEMOLISHALL, 0x00);
     av_delete(xc, MOUSE_XC_BUILDALL,    0x00);
@@ -111,6 +90,13 @@ mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stas
 
     /* update */
 
+    av_store(xc, MOUSE_XC_BUILDALL,    (SV*)buildall);
+    av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall);
+
+    attrall = mouse_calculate_all_attributes(aTHX_ metaclass);
+    SvREFCNT_inc_simple_void_NN(attrall);
+    av_store(xc, MOUSE_XC_ATTRALL,     (SV*)attrall);
+
     if(predicate_calls(metaclass, "is_immutable")){
         flags |= MOUSEf_XC_IS_IMMUTABLE;
     }
@@ -128,14 +114,10 @@ mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stas
     }
 
     av_store(xc, MOUSE_XC_FLAGS,       newSVuv(flags));
-    av_store(xc, MOUSE_XC_ATTRALL,     (SV*)attrall);
-    av_store(xc, MOUSE_XC_BUILDALL,    (SV*)buildall);
-    av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall);
 
     for(i = 0; i < len; i++){
         SV* const klass = MOUSE_av_at(linearized_isa, i);
         HV* const st    = gv_stashsv(klass, TRUE);
-        SV* meta;
         GV* gv;
 
         gv = stash_fetchs(st, "BUILD", FALSE);
@@ -148,14 +130,6 @@ mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stas
         if(gv && GvCVu(gv)){
             av_push(demolishall, newRV_inc((SV*)GvCV(gv)));
         }
-
-        /* ATTRIBUTES */
-        meta = get_metaclass(klass);
-        if(!SvOK(meta)){
-            continue; /* skip non-Mouse classes */
-        }
-
-        mouse_class_push_attribute_list(aTHX_ meta, attrall, seen);
     }
 
     FREETMPS;
@@ -236,13 +210,12 @@ mouse_buildargs(pTHX_ SV* metaclass, SV* const klass, I32 ax, I32 items) {
     else{
         I32 i;
 
-        args = newHV_mortal();
-
         if( (items % 2) != 0 ){
             if(!metaclass){ metaclass = get_metaclass(klass); }
             mouse_throw_error(metaclass, NULL, "Odd number of parameters to new()");
         }
 
+        args = newHV_mortal();
         for(i = 0; i < items; i += 2){
             (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U);
         }
@@ -297,7 +270,7 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const
     I32 const len   = AvFILLp(attrs) + 1;
     I32 i;
     AV* triggers_queue = NULL;
-    U32 used = 0;
+    I32 used = 0;
 
     assert(meta || object);
     assert(args);
@@ -352,7 +325,8 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const
         }
     } /* for each attribute */
 
-    if(MOUSE_xc_flags(xc) & MOUSEf_XC_CONSTRUCTOR_IS_STRICT && used < HvUSEDKEYS(args)){
+    if(MOUSE_xc_flags(xc) & MOUSEf_XC_CONSTRUCTOR_IS_STRICT
+            && used < (I32)HvUSEDKEYS(args)){
         mouse_report_unknown_args(aTHX_ meta, attrs, args);
     }
 
@@ -370,18 +344,16 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const
     if(MOUSE_xc_flags(xc) & MOUSEf_XC_IS_ANON){
         (void)set_slot(object, newSVpvs_flags("__METACLASS__", SVs_TEMP), meta);
     }
-
 }
 
-static SV*
+STATIC_INLINE SV*
 mouse_initialize_metaclass(pTHX_ SV* const klass) {
-    SV* meta = get_metaclass(klass);
-
-    if(!SvOK(meta)){
-        meta = mcall1s(newSVpvs_flags("Mouse::Meta::Class", SVs_TEMP), "initialize", klass);
+    SV* const meta = get_metaclass(klass);
+    if(LIKELY(SvOK(meta))){
+        return meta;
     }
-
-    return meta;
+    return mcall1s(newSVpvs_flags("Mouse::Meta::Class", SVs_TEMP),
+            "initialize", klass);
 }
 
 static void
index 6958375..4086d2b 100644 (file)
@@ -15,7 +15,7 @@ mouse_build_xa(pTHX_ SV* const attr) {
     ENTER;
     SAVETMPS;
 
-    xa    = newAV();
+    xa = newAV();
 
     mg = sv_magicext(SvRV(attr), (SV*)xa, PERL_MAGIC_ext, &mouse_xa_vtbl, NULL, 0);
     SvREFCNT_dec(xa); /* refcnt++ in sv_magicext */
@@ -319,7 +319,7 @@ CODE:
             "without a default, builder, or an init_arg", name);
     }
 
-     /* taken from Mouse::Meta::Attribute->new and ->_process_args */
+    /* taken from Mouse::Meta::Attribute->new and ->_process_args */
 
     svp = hv_fetchs(args, "is", FALSE);
     if(svp){
@@ -340,7 +340,7 @@ CODE:
             sv_setsv(*svp, name);
         }
         else if(strEQ(is, "bare")){
-            /* do nothing, but don't complain (later) about missing methods */
+            /* do nothing, but might complain later about missing methods */
         }
         else{
             mouse_throw_error(klass, NULL,
index 57ed21a..78ce1fd 100644 (file)
@@ -713,6 +713,17 @@ BOOT:
             code_ref );
     }
 
+UV
+_identity(SV* self, ...)
+CODE:
+{
+    if(!SvROK(self)) {
+        croak("Invalid object instance: '%"SVf"'", self);
+    }
+    RETVAL = PTR2UV(SvRV(self));
+}
+OUTPUT:
+    RETVAL
 
 void
 compile_type_constraint(SV* self)