Merged CMOP into Moose
[gitmo/Moose.git] / xs / HasMethods.xs
diff --git a/xs/HasMethods.xs b/xs/HasMethods.xs
new file mode 100644 (file)
index 0000000..0e617eb
--- /dev/null
@@ -0,0 +1,88 @@
+#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)
+{
+    char *method_name;
+    I32   method_name_len;
+    SV   *method;
+    HV   *symbols;
+
+    symbols = mop_get_all_package_symbols(stash, TYPE_FILTER_CODE);
+    sv_2mortal((SV*)symbols);
+
+    (void)hv_iterinit(map);
+    while ((method = hv_iternextsv(map, &method_name, &method_name_len))) {
+        SV *body;
+        SV *stash_slot;
+
+        if (!SvROK(method)) {
+            continue;
+        }
+
+        if (sv_isobject(method)) {
+            /* $method_object->body() */
+            body = mop_call0(aTHX_ method, KEY_FOR(body));
+        }
+        else {
+            body = method;
+        }
+
+        stash_slot = *hv_fetch(symbols, method_name, method_name_len, TRUE);
+        if (SvROK(stash_slot) && ((CV*)SvRV(body)) == ((CV*)SvRV(stash_slot))) {
+            continue;
+        }
+
+        /* $map->{$method_name} = undef */
+        sv_setsv(method, &PL_sv_undef);
+    }
+}
+
+MODULE = Class::MOP::Mixin::HasMethods   PACKAGE = Class::MOP::Mixin::HasMethods
+
+PROTOTYPES: DISABLE
+
+void
+_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");
+    INSTALL_SIMPLE_READER(Mixin::HasMethods, method_metaclass);
+    INSTALL_SIMPLE_READER(Mixin::HasMethods, wrapped_method_metaclass);