From: gfx Date: Tue, 3 Nov 2009 04:02:06 +0000 (+0900) Subject: Implement get_all_attributes in XS X-Git-Tag: 0.40_06~37 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=gitmo%2FMouse.git;a=commitdiff_plain;h=da4432f337513a1c3c791a4e6753a4b743cf3f62 Implement get_all_attributes in XS --- diff --git a/lib/Mouse/Meta/Class.pm b/lib/Mouse/Meta/Class.pm index da61cc4..eb0aadc 100644 --- a/lib/Mouse/Meta/Class.pm +++ b/lib/Mouse/Meta/Class.pm @@ -154,23 +154,6 @@ sub compute_all_applicable_attributes { return shift->get_all_attributes(@_) } -sub get_all_attributes { - my $self = shift; - my (@attr, %seen); - - for my $class ($self->linearized_isa) { - my $meta = Mouse::Util::get_metaclass_by_name($class) - or next; - - for my $name ($meta->get_attribute_list) { - next if $seen{$name}++; - push @attr, $meta->get_attribute($name); - } - } - - return @attr; -} - sub linearized_isa; sub new_object { diff --git a/lib/Mouse/PurePerl.pm b/lib/Mouse/PurePerl.pm index 7a41b08..1dbb7ab 100644 --- a/lib/Mouse/PurePerl.pm +++ b/lib/Mouse/PurePerl.pm @@ -211,6 +211,12 @@ sub roles { $_[0]->{roles} } sub linearized_isa { @{ get_linear_isa($_[0]->{package}) } } +sub get_all_attributes { + my($self) = @_; + my %attrs = map { %{ $self->initialize($_)->_attribute_map } } reverse $self->linearized_isa; + return values %attrs; +} + package Mouse::Meta::Role; diff --git a/mouse.h b/mouse.h index 9c12498..662e227 100644 --- a/mouse.h +++ b/mouse.h @@ -13,6 +13,10 @@ #define newSVpvs_share(s) Perl_newSVpvn_share(aTHX_ s, sizeof(s)-1, 0U) #endif +#ifndef get_cvs +#define get_cvs(name, flags) get_cv(name, flags) +#endif + #ifndef GvNAME_get #define GvNAME_get GvNAME #endif diff --git a/xs-src/Mouse.xs b/xs-src/Mouse.xs index a905f63..9886ec8 100644 --- a/xs-src/Mouse.xs +++ b/xs-src/Mouse.xs @@ -6,6 +6,98 @@ SV* mouse_namespace; SV* mouse_methods; SV* mouse_name; +static SV* mouse_all_attrs_cache; +static SV* mouse_all_attrs_cache_gen; + +AV* +mouse_get_all_attributes(pTHX_ SV* const metaclass){ + SV* const package = get_slot(metaclass, mouse_package); + HV* const stash = gv_stashsv(package, TRUE); + UV const pkg_gen = mro_get_pkg_gen(stash); + SV* cache_gen = get_slot(metaclass, mouse_all_attrs_cache_gen); + + if(!(cache_gen && pkg_gen == SvUV(cache_gen))){ /* update */ + CV* const get_metaclass = get_cvs("Mouse::Util::get_metaclass_by_name", TRUE); + AV* const linearized_isa = mro_get_linear_isa(stash); + I32 const len = AvFILLp(linearized_isa); + I32 i; + HV* seen; + AV* const all_attrs = newAV(); + + /* warn("Update all_attrs_cache (cache_gen %d != pkg_gen %d)", (cache_gen ? (int)SvIV(cache_gen) : 0), (int)pkg_gen); //*/ + + ENTER; + SAVETMPS; + + set_slot(metaclass, mouse_all_attrs_cache, sv_2mortal(newRV_inc((SV*)all_attrs))); + + seen = newHV(); + sv_2mortal((SV*)seen); + + for(i = 0; i < len; i++){ + SV* const klass = MOUSE_av_at(linearized_isa, i); + SV* meta; + I32 n; + dSP; + + PUSHMARK(SP); + XPUSHs(klass); + PUTBACK; + + call_sv((SV*)get_metaclass, G_SCALAR); + + SPAGAIN; + meta = POPs; + PUTBACK; + + if(!SvOK(meta)){ + continue; /* skip non-Mouse classes */ + } + + /* $meta->get_attribute_list */ + PUSHMARK(SP); + XPUSHs(meta); + PUTBACK; + + n = call_method("get_attribute_list", G_ARRAY); + 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(all_attrs, newSVsv( mcall1s(meta, "get_attribute", name) )); + } + } + + if(!cache_gen){ + cache_gen = sv_newmortal(); + } + sv_setuv(cache_gen, mro_get_pkg_gen(stash)); + set_slot(metaclass, mouse_all_attrs_cache_gen, cache_gen); + + FREETMPS; + LEAVE; + + return all_attrs; + } + else { + SV* const all_attrs_ref = get_slot(metaclass, mouse_all_attrs_cache); + + if(!IsArrayRef(all_attrs_ref)){ + croak("Not an ARRAY reference"); + } + + return (AV*)SvRV(all_attrs_ref); + } +} + MODULE = Mouse PACKAGE = Mouse PROTOTYPES: DISABLE @@ -16,6 +108,9 @@ BOOT: mouse_methods = newSVpvs_share("methods"); mouse_name = newSVpvs_share("name"); + mouse_all_attrs_cache = newSVpvs_share("__all_attrs_cache"); + mouse_all_attrs_cache_gen = newSVpvs_share("__all_attrs_cache_gen"); + MOUSE_CALL_BOOT(Mouse__Util); MOUSE_CALL_BOOT(Mouse__Util__TypeConstraints); MOUSE_CALL_BOOT(Mouse__Meta__Method__Accessor__XS); @@ -121,6 +216,19 @@ PPCODE: } } +void +get_all_attributes(SV* self) +PPCODE: +{ + AV* const all_attrs = mouse_get_all_attributes(aTHX_ self); + I32 const len = AvFILLp(all_attrs) + 1; + I32 i; + + EXTEND(SP, len); + for(i = 0; i < len; i++){ + PUSHs( MOUSE_av_at(all_attrs, i) ); + } +} MODULE = Mouse PACKAGE = Mouse::Meta::Role