Skip Menu |

This queue is for tickets about the Class-MOP CPAN distribution.

Report information
The Basics
Id: 41080
Status: resolved
Priority: 0/
Queue: Class-MOP

People
Owner: Nobody in particular
Requestors: gfuji [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: Class::MOP::Class->get_method_map() into MOP.xs
Hello, MOP maintainers. I've written a XS implementation of Class::MOP::Class->get_method_map(), indicated by tokuhirom(MATSUNO tokuhiro). I'll happy if you accept it. MOP.xs.diff is the patch with 0.70_01; xs_get_method_map.pl is a benchmark . Regards, -- Goro Fuji (GFUJI at CPAN.org)
Subject: MOP.xs.diff
--- MOP.xs.orig 2008-11-20 09:59:32.061118000 +0900 +++ MOP.xs 2008-11-20 10:35:54.122062000 +0900 @@ -1,3 +1,4 @@ +#define PERL_NO_GET_CONTEXT /* for efficiency */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" @@ -18,6 +19,138 @@ SV *key_body; U32 hash_body; + +/* method symbols */ +SV* s_method_metaclass; +SV* s_associated_metaclass; +SV* s_wrap; + + +#define check_package_cache_flag(stash) mop_check_package_cache_flag(aTHX_ stash) +#ifdef HvMROMETA /* 5.10.0 */ + +#ifndef mro_meta_init +#define mro_meta_inig(stash) Perl_mro_meta_init(aTHX_ stash) /* used in HvMROMETA macro */ +#endif /* !mro_meta_init */ + +static UV +mop_check_package_cache_flag(pTHX_ HV* stash){ + assert(SvTYPE(stash) == SVt_PVHV); + + return HvMROMETA(stash)->pkg_gen; /* mro::get_pkg_gen($pkg) */ +} +#else /* pre 5.10.0 */ + +static UV +mop_check_package_cache_flag(pTHX_ HV* stash){ + PERL_UNUSED_ARG(stash); + assert(SvTYPE(stash) == SVt_PVHV); + + return PL_sub_generation; +} +#endif + +#define call0(s, m) mop_call0(aTHX_ s, m) +#define call0s(s, m) mop_call0(aTHX_ s, sv_2mortal(newSVpvs(m))); +static SV* +mop_call0(pTHX_ SV* const self, SV* const method){ + dSP; + SV* ret; + + PUSHMARK(SP); + XPUSHs(self); + PUTBACK; + + call_sv(method, G_SCALAR | G_METHOD); + + SPAGAIN; + ret = POPs; + PUTBACK; + + return ret; +} + +static void +mop_update_method_map(pTHX_ SV* const self, SV* const class_name, HV* const stash, HV* const map){ + const char* const class_name_pv = HvNAME(stash); /* must be HvNAME(stash), not SvPV_nolen_const(class_name) */ + SV* method_metaclass = NULL; + char* name; + I32 namelen; + GV* gv; + dSP; + dXSTARG; /* used by PUSHp() macro */ + + hv_iterinit(stash); + while((gv = (GV*)hv_iternextsv(stash, &name, &namelen))){ + CV* cv; + if(SvROK(gv)){ /* special constants indroduced in 5.10.0 and backported into 5.8.9 */ + /* gv_init() enbodies a special constant and calls mro_method_changed_in(stash) */ + gv_init((GV*)gv, stash, name, namelen, GV_ADDMULTI); + } + + if(SvTYPE(gv) == SVt_PVGV && (cv = GvCVu(gv))){ + GV* const cvgv = CvGV(cv); /* ($cvpkg_name, $cv_name) = get_code_infor($cv) */ + const char* const cvpkg_name = HvNAME(GvSTASH(cvgv)); + const char* const cv_name = GvNAME(cvgv); + SV* method_slot; + SV* method_object; + + /* skip if the code does not come from this package */ + if(!(strEQ(cvpkg_name, "constant") && strEQ(cv_name, "__ANON__"))){ + if(strNE(cvpkg_name, class_name_pv)){ + continue; + } + } + + method_slot = *hv_fetch(map, name, namelen, TRUE); + if(SvOK(method_slot)){ + SV* const body = call0(method_slot, key_body); /* $method_object->body() */ + if(SvROK(body) && ((CV*)SvRV(body)) == cv){ + continue; + } + } + + if(!method_metaclass){ + method_metaclass = call0(self, s_method_metaclass); /* $self->method_metaclass() */ + } + + /* + $method_object = $method_metaclass->wrap( + $cv, + associated_metaclass => $self, + package_name => $class_name, + name => $method_name + ); + */ + ENTER; + SAVETMPS; + + PUSHMARK(SP); + EXTEND(SP, 8); + PUSHs(method_metaclass); /* invocant */ + mPUSHs(newRV_inc((SV*)cv)); + PUSHs(s_associated_metaclass); + PUSHs(self); + PUSHs(key_package_name); + PUSHs(class_name); + PUSHs(key_name); + PUSHp(name, namelen); /* use TARG */ + PUTBACK; + + call_sv(s_wrap, G_SCALAR | G_METHOD); + SPAGAIN; + method_object = POPs; + PUTBACK; + /* $map->{$method_name} = $method_object */ + sv_setsv(method_slot, method_object); + + FREETMPS; + LEAVE; + } + } +} + + /* get_code_info: Pass in a coderef, returns: @@ -38,6 +171,10 @@ PERL_HASH(hash_package, "package", 7); PERL_HASH(hash_package_name, "package_name", 12); + s_method_metaclass = newSVpvs("method_metaclass"); + s_wrap = newSVpvs("wrap"); + s_associated_metaclass = newSVpvs("associated_metaclass"); + PROTOTYPES: ENABLE @@ -247,3 +384,42 @@ XPUSHs(HeVAL(he)); else ST(0) = &PL_sv_undef; + + +MODULE = Class::MOP PACKAGE = Class::MOP::Class + +void +get_method_map(self) + SV* self +INIT: + if(!SvRV(self)){ + die("Cannot call get_method_map as a class method"); + } +CODE: +{ + HE* const he = hv_fetch_ent((HV*)SvRV(self), key_package, TRUE, hash_package); /* $self->name() */ + SV* const class_name = HeVAL(he); + HV* const stash = gv_stashsv(class_name, TRUE); + UV const current = check_package_cache_flag(stash); + SV* const cache_flag = *hv_fetchs((HV*)SvRV(self), "_package_cache_flag", TRUE); + SV* const map_ref = *hv_fetchs((HV*)SvRV(self), "methods", TRUE); + + if(!(SvROK(map_ref) && SvTYPE(SvRV(map_ref)) == SVt_PVHV)){ /* in a case "delete $meta->{method}" */ + SV* new_map_ref = newRV_noinc((SV*)newHV()); + sv_2mortal(new_map_ref); + sv_setsv(map_ref, new_map_ref); + } + + if(!(SvOK(cache_flag) && SvUV(cache_flag) == current)){ + ENTER; + SAVETMPS; + + mop_update_method_map(aTHX_ self, class_name, stash, (HV*)SvRV(map_ref)); + sv_setuv(cache_flag, check_package_cache_flag(stash)); /* update_cache_flag() */ + + FREETMPS; + LEAVE; + } + ST(0) = map_ref; /* map_ref is already mortal */ +} +
Subject: xs_get_method_map.pl
#!perl -w use strict; use Class::MOP::Class; BEGIN{ *Class::MOP::Class::pp_get_method_map = \&Class::MOP::Class::get_method_map } use Class::MOP; use Benchmark qw(:all); { package Foo; use metaclass; sub bar{42}; foreach my $n(1 .. 10){ no strict 'refs'; *{'baz' . $n} = \&bar; } } print "Initialization:\n"; my $meta = Foo->meta; cmpthese -1 => { xs => sub{ $meta->reset_package_cache_flag; %{$meta->{methods}} = (); my $map = $meta->get_method_map(); }, pp => sub{ $meta->reset_package_cache_flag; %{$meta->{methods}} = (); my $map = $meta->pp_get_method_map(); }, }; print "\n", "Looking into the stash:\n"; cmpthese -1 => { xs => sub{ $meta->reset_package_cache_flag; my $map = $meta->get_method_map(); }, pp => sub{ $meta->reset_package_cache_flag; my $map = $meta->pp_get_method_map(); }, }; print "\n", "Getting the cache:\n"; cmpthese -1 => { xs => sub{ my $map = $meta->get_method_map(); }, pp => sub{ my $map = $meta->pp_get_method_map(); }, };