1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
#include "mop.h"
SV *mop_method_metaclass;
SV *mop_associated_metaclass;
SV *mop_wrap;
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_name;
char *method_name;
I32 method_name_len;
SV *coderef;
HV *symbols;
dSP;
symbols = mop_get_all_package_symbols(stash, TYPE_FILTER_CODE);
sv_2mortal((SV*)symbols);
(void)hv_iterinit(symbols);
while ( (coderef = hv_iternextsv(symbols, &method_name, &method_name_len)) ) {
CV *cv = (CV *)SvRV(coderef);
char *cvpkg_name;
char *cv_name;
SV *method_slot;
SV *method_object;
if (!mop_get_code_info(coderef, &cvpkg_name, &cv_name)) {
continue;
}
/* this checks to see that the subroutine is actually from our package */
if ( !(strEQ(cvpkg_name, "constant") && strEQ(cv_name, "__ANON__")) ) {
if ( strNE(cvpkg_name, class_name_pv) ) {
continue;
}
}
method_slot = *hv_fetch(map, method_name, method_name_len, TRUE);
if ( SvOK(method_slot) ) {
SV *body;
if ( sv_isobject(method_slot) ) {
body = mop_call0(aTHX_ method_slot, KEY_FOR(body)); /* $method_object->body() */
}
else {
body = method_slot;
}
if ( SvROK(body) && ((CV *) SvRV(body)) == cv ) {
continue;
}
}
method_metaclass_name = mop_call0(aTHX_ self, mop_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_name); /* invocant */
mPUSHs(newRV_inc((SV *)cv));
PUSHs(mop_associated_metaclass);
PUSHs(self);
PUSHs(KEY_FOR(package_name));
PUSHs(class_name);
PUSHs(KEY_FOR(name));
mPUSHs(newSVpv(method_name, method_name_len));
PUTBACK;
call_sv(mop_wrap, G_SCALAR | G_METHOD);
SPAGAIN;
method_object = POPs;
PUTBACK;
/* $map->{$method_name} = $method_object */
sv_setsv(method_slot, method_object);
FREETMPS;
LEAVE;
}
}
MODULE = Class::MOP::Mixin::HasMethods PACKAGE = Class::MOP::Mixin::HasMethods
PROTOTYPES: DISABLE
void
_full_method_map(self)
SV *self
PREINIT:
HV *const obj = (HV *)SvRV(self);
SV *const class_name = HeVAL( hv_fetch_ent(obj, KEY_FOR(package), 0, HASH_FOR(package)) );
HV *const stash = gv_stashsv(class_name, 0);
UV current;
SV *cache_flag;
SV *map_ref;
PPCODE:
if (!stash) {
mXPUSHs(newRV_noinc((SV *)newHV()));
return;
}
current = mop_check_package_cache_flag(aTHX_ stash);
cache_flag = HeVAL( hv_fetch_ent(obj, KEY_FOR(package_cache_flag), TRUE, HASH_FOR(package_cache_flag)));
map_ref = HeVAL( hv_fetch_ent(obj, KEY_FOR(methods), TRUE, HASH_FOR(methods)));
/* $self->{methods} does not yet exist (or got deleted) */
if ( !SvROK(map_ref) || SvTYPE(SvRV(map_ref)) != SVt_PVHV ) {
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 ) {
mop_update_method_map(aTHX_ self, class_name, stash, (HV *)SvRV(map_ref));
sv_setuv(cache_flag, mop_check_package_cache_flag(aTHX_ stash)); /* update_cache_flag() */
}
XPUSHs(map_ref);
BOOT:
mop_method_metaclass = newSVpvs("method_metaclass");
mop_associated_metaclass = newSVpvs("associated_metaclass");
mop_wrap = newSVpvs("wrap");
|