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();
},
};