X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=xs-src%2FMouse.xs;h=8bb69c56b948453b013160cc5b0761efc31d6fbe;hb=902174eb6dc2afbe8a2b8fb0a27446b3276787d8;hp=143228a794e24cfa541d03e04407e867f22bbe56;hpb=70425827d676b2b4130f5e78689bf017fe4c4609;p=gitmo%2FMouse.git diff --git a/xs-src/Mouse.xs b/xs-src/Mouse.xs index 143228a..8bb69c5 100644 --- a/xs-src/Mouse.xs +++ b/xs-src/Mouse.xs @@ -41,6 +41,12 @@ enum mouse_xc_ix_t{ MOUSE_XC_last }; +enum mouse_modifier_t { + MOUSE_M_BEFORE, + MOUSE_M_AROUND, + MOUSE_M_AFTER, +}; + static MGVTBL mouse_xc_vtbl; /* for identity */ static void @@ -70,10 +76,10 @@ mouse_class_push_attribute_list(pTHX_ SV* const metaclass, AV* const attrall, HV } } +XS(XS_Mouse__Object_BUILDARGS); /* prototype */ + static int mouse_class_has_custom_buildargs(pTHX_ HV* const stash) { - XS(XS_Mouse__Object_BUILDARGS); /* prototype */ - GV* const buildargs = gv_fetchmeth_autoload(stash, "BUILDARGS", sizeof("BUILDARGS")-1, 0); return buildargs && CvXSUB(GvCV(buildargs)) != XS_Mouse__Object_BUILDARGS; @@ -117,7 +123,7 @@ mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stas flags |= MOUSEf_XC_HAS_BUILDARGS; } - if(predicate_calls(metaclass, "__strict_constructor")){ + if(predicate_calls(metaclass, "strict_constructor")){ flags |= MOUSEf_XC_CONSTRUCTOR_IS_STRICT; } @@ -285,13 +291,13 @@ mouse_report_unknown_args(pTHX_ SV* const meta, AV* const attrs, HV* const args) static void -mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const ignore_triggers) { +mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const is_cloning) { AV* const xc = mouse_get_xc(aTHX_ meta); AV* const attrs = MOUSE_xc_attrall(xc); - I32 len = AvFILLp(attrs) + 1; + I32 const len = AvFILLp(attrs) + 1; I32 i; AV* triggers_queue = NULL; - I32 used = 0; + U32 used = 0; assert(meta || object); assert(args); @@ -301,10 +307,6 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const croak("You cannot use tied HASH reference as initializing arguments"); } - if(!ignore_triggers){ - triggers_queue = newAV_mortal(); - } - /* for each attribute */ for(i = 0; i < len; i++){ SV* const attr = MOUSE_av_at(attrs, i); @@ -324,22 +326,27 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const if(SvROK(value) && flags & MOUSEf_ATTR_IS_WEAK_REF){ weaken_slot(object, slot); } - if(flags & MOUSEf_ATTR_HAS_TRIGGER && triggers_queue){ + if(flags & MOUSEf_ATTR_HAS_TRIGGER){ AV* const pair = newAV(); av_push(pair, newSVsv( mcall0s(attr, "trigger") )); av_push(pair, newSVsv(value)); + if(!triggers_queue) { + triggers_queue = newAV_mortal(); + } av_push(triggers_queue, (SV*)pair); } used++; } else { /* no init arg */ if(flags & (MOUSEf_ATTR_HAS_DEFAULT | MOUSEf_ATTR_HAS_BUILDER)){ - if(!(flags & MOUSEf_ATTR_IS_LAZY)){ + /* skip if the object has the slot (it occurs on cloning/reblessing) */ + if(!(flags & MOUSEf_ATTR_IS_LAZY) && !has_slot(object, slot)){ mouse_xa_set_default(aTHX_ xa, object); } } - else if(flags & MOUSEf_ATTR_IS_REQUIRED) { + /* don't check while cloning (or reblesseing) */ + else if(!is_cloning && flags & MOUSEf_ATTR_IS_REQUIRED) { mouse_throw_error(attr, NULL, "Attribute (%"SVf") is required", slot); } } @@ -350,7 +357,7 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const } if(triggers_queue){ - len = AvFILLp(triggers_queue) + 1; + I32 const len = AvFILLp(triggers_queue) + 1; for(i = 0; i < len; i++){ AV* const pair = (AV*)AvARRAY(triggers_queue)[i]; SV* const trigger = AvARRAY(pair)[0]; @@ -371,18 +378,7 @@ mouse_initialize_metaclass(pTHX_ SV* const klass) { SV* meta = get_metaclass(klass); if(!SvOK(meta)){ - dSP; - PUSHMARK(SP); - - EXTEND(SP, 2); - mPUSHp("Mouse::Meta::Class", sizeof("Mouse::Meta::Class")-1); - PUSHs(klass); - PUTBACK; - - call_method("initialize", G_SCALAR); - SPAGAIN; - meta = POPs; - PUTBACK; + meta = mcall1s(newSVpvs_flags("Mouse::Meta::Class", SVs_TEMP), "initialize", klass); } return meta; @@ -402,7 +398,7 @@ mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) { PUSHs(args); PUTBACK; - call_sv(AvARRAY(buildall)[i], G_VOID); + call_sv_safe(AvARRAY(buildall)[i], G_VOID); /* discard a scalar which G_VOID returns */ SPAGAIN; @@ -411,11 +407,65 @@ mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) { } } +static AV* +mouse_get_modifier_storage(pTHX_ + SV* const meta, + enum mouse_modifier_t const m, SV* const name) { + static const char* const keys[] = { + "before", + "around", + "after", + }; + SV* const key = sv_2mortal(Perl_newSVpvf(aTHX_ "%s_method_modifiers", keys[m])); + SV* table; + SV* storage_ref; + + must_defined(name, "a method name"); + + table = get_slot(meta, key); + + if(!table){ + /* $meta->{$key} = {} */ + table = sv_2mortal(newRV_noinc((SV*)newHV())); + set_slot(meta, key, table); + } + + storage_ref = get_slot(table, name); + + if(!storage_ref){ + storage_ref = sv_2mortal(newRV_noinc((SV*)newAV())); + set_slot(table, name, storage_ref); + } + else{ + if(!IsArrayRef(storage_ref)){ + croak("Modifier strorage for '%s' is not an ARRAY reference", keys[m]); + } + } + + return (AV*)SvRV(storage_ref); +} + +static void +XS_Mouse_value_holder(pTHX_ CV* const cv PERL_UNUSED_DECL) { + dVAR; dXSARGS; + SV* const value = (SV*)XSANY.any_ptr; + assert(value); + PERL_UNUSED_VAR(items); + ST(0) = value; + XSRETURN(1); +} + +DECL_BOOT(Mouse__Util); +DECL_BOOT(Mouse__Util__TypeConstraints); +DECL_BOOT(Mouse__Meta__Method__Accessor__XS); +DECL_BOOT(Mouse__Meta__Attribute); + MODULE = Mouse PACKAGE = Mouse PROTOTYPES: DISABLE BOOT: +{ mouse_package = newSVpvs_share("package"); mouse_namespace = newSVpvs_share("namespace"); mouse_methods = newSVpvs_share("methods"); @@ -425,11 +475,11 @@ BOOT: mouse_get_attribute = newSVpvs_share("get_attribute"); mouse_get_attribute_list = newSVpvs_share("get_attribute_list"); - MOUSE_CALL_BOOT(Mouse__Util); - MOUSE_CALL_BOOT(Mouse__Util__TypeConstraints); - MOUSE_CALL_BOOT(Mouse__Meta__Method__Accessor__XS); - MOUSE_CALL_BOOT(Mouse__Meta__Attribute); - + CALL_BOOT(Mouse__Util); + CALL_BOOT(Mouse__Util__TypeConstraints); + CALL_BOOT(Mouse__Meta__Method__Accessor__XS); + CALL_BOOT(Mouse__Meta__Attribute); +} MODULE = Mouse PACKAGE = Mouse::Meta::Module @@ -465,84 +515,64 @@ CODE: croak("No package name defined"); } - SvGETMAGIC(name); - SvGETMAGIC(code); - - if(!SvOK(name)){ - mouse_throw_error(self, NULL, "You must define a method name"); - } - if(!SvROK(code)){ - mouse_throw_error(self, NULL, "You must define a CODE reference"); - } + must_defined(name, "a method name"); + must_ref (code, "a CODE reference", SVt_NULL); /* any reftype is OK */ code_ref = code; if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){ SV* sv = code_ref; /* used in tryAMAGICunDEREF */ SV** sp = &sv; /* used in tryAMAGICunDEREF */ tryAMAGICunDEREF(to_cv); /* try \&{$code} */ - if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)){ - mouse_throw_error(self, NULL, "You must pass a CODE reference to add_method"); - } + must_ref(code, "a CODE reference", SVt_PVCV); code_ref = sv; } /* *{$package . '::' . $name} -> *gv */ gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV); - if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */ - SvREFCNT_dec(GvCV(gv)); - GvCV(gv) = NULL; - } - sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */ - + mouse_install_sub(aTHX_ gv, code_ref); + //CvMETHOD_on((CV*)SvRV(code_ref)); (void)set_slot(methods, name, code); /* $self->{methods}{$name} = $code */ +} - /* name the CODE ref if it's anonymous */ - { - CV* const code_entity = (CV*)SvRV(code_ref); - if(CvANON(code_entity) - && CvGV(code_entity) /* a cv under construction has no gv */ ){ - HV* dbsub; - - /* update %DB::sub to make NYTProf happy */ - if((PL_perldb & (PERLDBf_SUBLINE|PERLDB_NAMEANON)) - && PL_DBsub && (dbsub = GvHV(PL_DBsub)) - ){ - /* see Perl_newATTRSUB() in op.c */ - SV* const subname = sv_newmortal(); - HE* orig; - - gv_efullname3(subname, CvGV(code_entity), NULL); - orig = hv_fetch_ent(dbsub, subname, FALSE, 0U); - if(orig){ - gv_efullname3(subname, gv, NULL); - (void)hv_store_ent(dbsub, subname, HeVAL(orig), 0U); - SvREFCNT_inc_simple_void_NN(HeVAL(orig)); - } - } - - CvGV(code_entity) = gv; - CvANON_off(code_entity); - } - } +void +add_class_accessor(SV* self, SV* name) +CODE: +{ + SV* const klass = mouse_call0(self, mouse_name); + const char* fq_name = form("%"SVf"::%"SVf, klass, name); + STRLEN keylen; + const char* const key = SvPV_const(name, keylen); + mouse_simple_accessor_generate(aTHX_ fq_name, key, keylen, + XS_Mouse_inheritable_class_accessor, NULL, 0); } MODULE = Mouse PACKAGE = Mouse::Meta::Class BOOT: +{ + CV* xsub; + INSTALL_SIMPLE_READER(Class, roles); INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id); INSTALL_SIMPLE_READER(Class, is_immutable); - INSTALL_SIMPLE_READER_WITH_KEY(Class, __strict_constructor, strict_constructor); + + INSTALL_INHERITABLE_CLASS_ACCESSOR(strict_constructor); INSTALL_CLASS_HOLDER(Class, method_metaclass, "Mouse::Meta::Method"); INSTALL_CLASS_HOLDER(Class, attribute_metaclass, "Mouse::Meta::Attribute"); INSTALL_CLASS_HOLDER(Class, constructor_class, "Mouse::Meta::Method::Constructor::XS"); INSTALL_CLASS_HOLDER(Class, destructor_class, "Mouse::Meta::Method::Destructor::XS"); - newCONSTSUB(gv_stashpvs("Mouse::Meta::Method::Constructor::XS", TRUE), "_generate_constructor", - newRV_inc((SV*)get_cvs("Mouse::Object::new", TRUE))); - newCONSTSUB(gv_stashpvs("Mouse::Meta::Method::Destructor::XS", TRUE), "_generate_destructor", - newRV_inc((SV*)get_cvs("Mouse::Object::DESTROY", TRUE))); + xsub = newXS("Mouse::Meta::Method::Constructor::XS::_generate_constructor", + XS_Mouse_value_holder, file); + CvXSUBANY(xsub).any_ptr + = newRV_inc((SV*)get_cvs("Mouse::Object::new", GV_ADD)); + + xsub = newXS("Mouse::Meta::Method::Destructor::XS::_generate_destructor", + XS_Mouse_value_holder, file); + CvXSUBANY(xsub).any_ptr + = newRV_inc((SV*)get_cvs("Mouse::Object::DESTROY", GV_ADD)); +} void @@ -579,24 +609,46 @@ PPCODE: } } -SV* +void new_object(SV* meta, ...) CODE: { AV* const xc = mouse_get_xc(aTHX_ meta); HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax, items); + SV* object; - RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); - mouse_class_initialize_object(aTHX_ meta, RETVAL, args, FALSE); + object = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); + mouse_class_initialize_object(aTHX_ meta, object, args, FALSE); + mouse_buildall(aTHX_ xc, object, sv_2mortal(newRV_inc((SV*)args))); /* BUILDALL */ + ST(0) = object; /* because object is mortal, we should return it as is */ + XSRETURN(1); } -OUTPUT: - RETVAL void -_initialize_object(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE) +clone_object(SV* meta, SV* object, ...) +CODE: +{ + AV* const xc = mouse_get_xc(aTHX_ meta); + HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax + 1, items - 1); + SV* proto; + + if(!mouse_is_an_instance_of(aTHX_ MOUSE_xc_stash(xc), object)) { + mouse_throw_error(meta, object, + "You must pass an instance of the metaclass (%"SVf"), not (%"SVf")", + mcall0(meta, mouse_name), object); + } + + proto = mouse_instance_clone(aTHX_ object); + mouse_class_initialize_object(aTHX_ meta, proto, args, TRUE); + ST(0) = proto; /* because object is mortal, we should return it as is */ + XSRETURN(1); +} + +void +_initialize_object(SV* meta, SV* object, HV* args, bool is_cloning = FALSE) CODE: { - mouse_class_initialize_object(aTHX_ meta, object, args, ignore_triggers); + mouse_class_initialize_object(aTHX_ meta, object, args, is_cloning); } MODULE = Mouse PACKAGE = Mouse::Meta::Role @@ -607,9 +659,42 @@ BOOT: INSTALL_CLASS_HOLDER(Role, method_metaclass, "Mouse::Meta::Role::Method"); +void +add_before_modifier(SV* self, SV* name, SV* modifier) +CODE: +{ + av_push(mouse_get_modifier_storage(aTHX_ self, (enum mouse_modifier_t)ix, name), newSVsv(modifier)); +} +ALIAS: + add_before_method_modifier = MOUSE_M_BEFORE + add_around_method_modifier = MOUSE_M_AROUND + add_after_method_modifier = MOUSE_M_AFTER + +void +get_before_modifiers(SV* self, SV* name) +ALIAS: + get_before_method_modifiers = MOUSE_M_BEFORE + get_around_method_modifiers = MOUSE_M_AROUND + get_after_method_modifiers = MOUSE_M_AFTER +PPCODE: +{ + AV* const storage = mouse_get_modifier_storage(aTHX_ self, (enum mouse_modifier_t)ix, name); + I32 const len = av_len(storage) + 1; + if(GIMME_V == G_ARRAY) { + I32 i; + EXTEND(SP, len); + for(i = 0; i < len; i++){ + PUSHs(*av_fetch(storage, i, TRUE)); + } + } + else{ + mPUSHi(len); + } +} + MODULE = Mouse PACKAGE = Mouse::Object -SV* +void new(SV* klass, ...) CODE: { @@ -617,6 +702,7 @@ CODE: AV* const xc = mouse_get_xc(aTHX_ meta); UV const flags = MOUSE_xc_flags(xc); SV* args; + SV* object; /* BUILDARGS */ if(flags & MOUSEf_XC_HAS_BUILDARGS){ @@ -628,9 +714,10 @@ CODE: for(i = 0; i < items; i++){ PUSHs(ST(i)); } - //SP += items; + PUTBACK; - call_method("BUILDARGS", G_SCALAR); + call_method_safes("BUILDARGS", G_SCALAR); + SPAGAIN; args = POPs; PUTBACK; @@ -645,13 +732,12 @@ CODE: } /* new_object */ - RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); - mouse_class_initialize_object(aTHX_ meta, RETVAL, (HV*)SvRV(args), FALSE); - - mouse_buildall(aTHX_ xc, RETVAL, args); + object = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); + mouse_class_initialize_object(aTHX_ meta, object, (HV*)SvRV(args), FALSE); + mouse_buildall(aTHX_ xc, object, args); /* BUILDALL */ + ST(0) = object; /* because object is mortal, we should return it as is */ + XSRETURN(1); } -OUTPUT: - RETVAL void DESTROY(SV* object) @@ -692,21 +778,21 @@ CODE: len = AvFILLp(demolishall) + 1; if(len > 0){ - GV* const statusvalue = gv_fetchpvs("?", 0, SVt_PV); + SV* const in_global_destruction = boolSV(PL_dirty); + SAVEI32(PL_statusvalue); /* local $? */ + PL_statusvalue = 0; - if(statusvalue){ /* it can be NULL */ - SAVESPTR(GvSV(statusvalue)); /* local $? */ - GvSV(statusvalue) = sv_newmortal(); - } SAVESPTR(ERRSV); /* local $@ */ - ERRSV = newSVpvs_flags("", SVs_TEMP); + ERRSV = sv_newmortal(); + + EXTEND(SP, 2); for(i = 0; i < len; i++){ SPAGAIN; PUSHMARK(SP); - XPUSHs(object); - XPUSHs(boolSV(PL_dirty)); + PUSHs(object); + PUSHs(in_global_destruction); PUTBACK; call_sv(AvARRAY(demolishall)[i], G_VOID | G_EVAL); @@ -747,8 +833,6 @@ CODE: SV* const meta = get_metaclass(self); AV* const xc = mouse_get_xc(aTHX_ meta); - if(!IsHashRef(args)){ - croak("You must pass a HASH reference to BUILDALL"); - } + must_ref(args, "a HASH reference to BUILDALL", SVt_PVHV); mouse_buildall(aTHX_ xc, self, args); }