--- /dev/null
+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;
$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;
+ foreach my $attr($self->get_all_attributes) {
+ return $attr if $attr->name eq $name;
}
- return $attr;
+ return undef;
}
sub add_attribute {
$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;
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', __LINE__, __FILE__, $metaclass->name, $buildargs, $buildall);
#line %d %s
package %s;
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]} : @_);
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;
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 */
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);
/* 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;
}
}
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);
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;