X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=gitmo%2FMouse.git;a=blobdiff_plain;f=xs-src%2FMouse.xs;h=331b5bf6d267f32ea22743a66312c2d0fc6ef714;hp=e518b03c9c1733aa9eeb6afe7986a2a2a22392c8;hb=787f84c2e6971ebb69a12663cddbf3307c1ca371;hpb=4e7e3250fdc8eeccfd656270b40f6aa9817da9a9 diff --git a/xs-src/Mouse.xs b/xs-src/Mouse.xs index e518b03..331b5bf 100644 --- a/xs-src/Mouse.xs +++ b/xs-src/Mouse.xs @@ -13,7 +13,7 @@ SV* mouse_get_attribute_list; #define MOUSE_xc_stash(a) ( (HV*)MOUSE_av_at((a), MOUSE_XC_STASH) ) #define MOUSE_xc_attrall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_ATTRALL) ) #define MOUSE_xc_buildall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_BUILDALL) ) -#define MOUSE_xc_demolishall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_DEOLISHALL) ) +#define MOUSE_xc_demolishall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_DEMOLISHALL) ) enum mouse_xc_flags_t { MOUSEf_XC_IS_IMMUTABLE = 0x0001, @@ -30,8 +30,6 @@ enum mouse_xc_ix_t{ MOUSE_XC_GEN, /* class generation */ MOUSE_XC_STASH, /* symbol table hash */ - MOUSE_XC_BUILDARGS, /* Custom BUILDARGS */ - MOUSE_XC_ATTRALL, /* all the attributes */ MOUSE_XC_BUILDALL, /* all the BUILD methods */ MOUSE_XC_DEMOLISHALL, /* all the DEMOLISH methods */ @@ -74,7 +72,7 @@ mouse_class_has_custom_buildargs(pTHX_ HV* const stash) { GV* const buildargs = gv_fetchmeth_autoload(stash, "BUILDARGS", sizeof("BUILDARGS")-1, 0); - return buildargs && CvXSUB(GvCV(buildargs)) == XS_Mouse__Object_BUILDARGS; + return buildargs && CvXSUB(GvCV(buildargs)) != XS_Mouse__Object_BUILDARGS; } static void @@ -122,15 +120,17 @@ mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stas for(i = 0; i < len; i++){ SV* const klass = MOUSE_av_at(linearized_isa, i); + HV* const st = gv_stashsv(klass, TRUE); SV* meta; GV* gv; - gv = stash_fetchs(stash, "BUILD", FALSE); + gv = stash_fetchs(st, "BUILD", FALSE); if(gv && GvCVu(gv)){ - av_push(buildall, newRV_inc((SV*)GvCV(gv))); + av_unshift(buildall, 1); + av_store(buildall, 0, newRV_inc((SV*)GvCV(gv))); } - gv = stash_fetchs(stash, "DEMOLISH", FALSE); + gv = stash_fetchs(st, "DEMOLISH", FALSE); if(gv && GvCVu(gv)){ av_push(demolishall, newRV_inc((SV*)GvCV(gv))); } @@ -150,7 +150,7 @@ mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stas sv_setuv(MOUSE_xc_gen(xc), mro_get_pkg_gen(stash)); } -AV* +static AV* mouse_get_xc(pTHX_ SV* const metaclass) { AV* xc; SV* gen; @@ -175,7 +175,7 @@ mouse_get_xc(pTHX_ SV* const metaclass) { av_extend(xc, MOUSE_XC_last - 1); - av_store(xc, MOUSE_XC_GEN, newSViv(0)); + av_store(xc, MOUSE_XC_GEN, newSVuv(0U)); av_store(xc, MOUSE_XC_STASH, (SV*)stash); SvREFCNT_inc_simple_void_NN(stash); @@ -188,20 +188,30 @@ mouse_get_xc(pTHX_ SV* const metaclass) { } gen = MOUSE_xc_gen(xc); + + if(SvUVX(gen) != 0U && MOUSE_xc_flags(xc) & MOUSEf_XC_IS_IMMUTABLE){ + return xc; + } + stash = MOUSE_xc_stash(xc); - if(SvUV(gen) != mro_get_pkg_gen(stash)){ + if(SvUVX(gen) != mro_get_pkg_gen(stash)){ mouse_class_update_xc(aTHX_ metaclass, stash, xc); } return xc; } -HV* -mouse_build_args(pTHX_ SV* metaclass, SV* const klass, I32 const start, I32 const items, I32 const ax) { +static HV* +mouse_buildargs(pTHX_ SV* metaclass, SV* const klass, I32 ax, I32 items) { HV* args; - if((items - start) == 1){ - SV* const args_ref = ST(start); + + /* shift @_ */ + ax++; + items--; + + if(items == 1){ + SV* const args_ref = ST(0); if(!IsHashRef(args_ref)){ if(!metaclass){ metaclass = get_metaclass(klass); } mouse_throw_error(metaclass, NULL, "Single parameters to new() must be a HASH ref"); @@ -214,12 +224,12 @@ mouse_build_args(pTHX_ SV* metaclass, SV* const klass, I32 const start, I32 cons args = newHV_mortal(); - if( ((items - start) % 2) != 0 ){ + if( (items % 2) != 0 ){ if(!metaclass){ metaclass = get_metaclass(klass); } mouse_throw_error(metaclass, NULL, "Odd number of parameters to new()"); } - for(i = start; i < items; i += 2){ + for(i = 0; i < items; i += 2){ (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U); } @@ -227,7 +237,7 @@ mouse_build_args(pTHX_ SV* metaclass, SV* const klass, I32 const start, I32 cons return args; } -void +static void mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const ignore_triggers) { AV* const xc = mouse_get_xc(aTHX_ meta); AV* const attrs = MOUSE_xc_attrall(xc); @@ -235,6 +245,10 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const I32 i; AV* triggers_queue = NULL; + assert(meta || object); + assert(args); + assert(SvTYPE(args) == SVt_PVHV); + ENTER; SAVETMPS; @@ -243,8 +257,8 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const } for(i = 0; i < len; i++){ - SV* const attr = AvARRAY(attrs)[i]; - AV* const xa = mouse_get_xa(aTHX_ AvARRAY(attrs)[i]); + SV* const attr = MOUSE_av_at(attrs, i); + AV* const xa = mouse_get_xa(aTHX_ attr); SV* const slot = MOUSE_xa_slot(xa); U16 const flags = (U16)MOUSE_xa_flags(xa); @@ -299,6 +313,144 @@ mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const LEAVE; } +static SV* +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; + } + + return meta; +} + + +/* copied from Class-MOP/topic/symbol-manipluator */ +static GV* +mouse_get_gv(pTHX_ SV* const self, svtype const type, const char* const var_name, I32 const var_name_len, I32 const flags){ + SV* package_name; + STRLEN len; + const char* pv; + + if(!flags){ + SV* const ns = mcall0(self, mouse_namespace); + GV** gvp; + if(!(SvROK(ns) && SvTYPE(SvRV(ns)) == SVt_PVHV)){ + croak("namespace() did not return a hash reference"); + } + gvp = (GV**)hv_fetch((HV*)SvRV(ns), var_name, var_name_len, FALSE); + if(gvp && isGV_with_GP(*gvp)){ + return *gvp; + } + } + + package_name = get_slot(self, mouse_package); + + if(!(package_name && SvOK(package_name))){ + croak("No package name defined"); + } + + pv = SvPV_const(package_name, len); + + return gv_fetchpvn_flags(Perl_form(aTHX_ "%s::%s", pv, var_name), (len + var_name_len + 2), flags, type); +} + +/* copied from Class-MOP/topic/symbol-manipluator */ +static SV* +mouse_gv_elem(pTHX_ GV* const gv, svtype const type, I32 const add){ + SV* sv; + + if(!gv){ + return NULL; + } + + assert(isGV_with_GP(gv)); + + switch(type){ + case SVt_PVAV: + sv = (SV*)(add ? GvAVn(gv) : GvAV(gv)); + break; + case SVt_PVHV: + sv = (SV*)(add ? GvHVn(gv) : GvHV(gv)); + break; + case SVt_PVCV: + sv = (SV*)GvCV(gv); + break; + case SVt_PVIO: + sv = (SV*)(add ? GvIOn(gv) : GvIO(gv)); + break; + case SVt_PVGV: + sv = (SV*)gv; + break; + default: /* SCALAR */ + sv = add ? GvSVn(gv) : GvSV(gv); + break; + } + + return sv; +} + +/* copied from Class-MOP/topic/symbol-manipluator */ +static void +mouse_deconstruct_variable_name(pTHX_ SV* const variable, + const char** const var_name, STRLEN* const var_name_len, + svtype* const type, + const char** const type_name) { + + STRLEN len; + const char* pv; + + /* e.g. variable = '$foo' */ + + if(!SvOK(variable)) { + croak("You must pass a variable name"); + } + pv = SvPV_const(variable, len); + if(len < 2){ + croak("You must pass a variable name including a sigil"); + } + + *var_name = pv + 1; + *var_name_len = len - 1; + + switch(pv[0]){ + case '$': + *type = SVt_PV; /* for all the types of scalars */ + *type_name = "SCALAR"; + break; + case '@': + *type = SVt_PVAV; + *type_name = "ARRAY"; + break; + case '%': + *type = SVt_PVHV; + *type_name = "HASH"; + break; + case '&': + *type = SVt_PVCV; + *type_name = "CODE"; + break; + case '*': + *type = SVt_PVGV; + *type_name = "GLOB"; + break; + default: + croak("I do not recognize that sigil '%c'", pv[0]); + } +} + + MODULE = Mouse PACKAGE = Mouse PROTOTYPES: DISABLE @@ -367,8 +519,8 @@ CODE: 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, "Not a CODE reference"); + if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)){ + mouse_throw_error(self, NULL, "You must pass a CODE reference to add_method"); } code_ref = sv; } @@ -383,23 +535,69 @@ CODE: set_slot(methods, name, code); /* $self->{methods}{$name} = $code */ - /* TODO: name the CODE ref if it's anonymous */ - //code_entity = (CV*)SvRV(code_ref); - //if(CvANON(code_entity) - // && CvGV(code_entity) /* a cv under construction has no gv */ ){ + /* 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 */ ){ - // CvGV(code_entity) = gv; - // CvANON_off(code_entity); - //} + CvGV(code_entity) = gv; + CvANON_off(code_entity); + } + } } +bool +has_package_symbol(SV* self, SV* variable) +PREINIT: + svtype type; + const char* type_name; + const char* var_name; + STRLEN var_name_len; + GV* gv; +CODE: + mouse_deconstruct_variable_name(aTHX_ variable, &var_name, &var_name_len, &type, &type_name); + gv = mouse_get_gv(aTHX_ self, type, var_name, var_name_len, 0); + RETVAL = mouse_gv_elem(aTHX_ gv, type, FALSE) ? TRUE : FALSE; +OUTPUT: + RETVAL + +SV* +get_package_symbol(SV* self, SV* variable) +PREINIT: + svtype type; + const char* type_name; + const char* var_name; + STRLEN var_name_len; + I32 flags = 0; + GV* gv; + SV* sv; +CODE: + mouse_deconstruct_variable_name(aTHX_ variable, &var_name, &var_name_len, &type, &type_name); + gv = mouse_get_gv(aTHX_ self, type, var_name, var_name_len, flags); + sv = mouse_gv_elem(aTHX_ gv, type, FALSE); + + RETVAL = sv ? newRV_inc(sv) : &PL_sv_undef; +OUTPUT: + RETVAL + + MODULE = Mouse PACKAGE = Mouse::Meta::Class BOOT: INSTALL_SIMPLE_READER(Class, roles); INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id); - newCONSTSUB(gv_stashpvs("Mouse::Meta::Class", TRUE), "constructor_class", - newSVpvs("Mouse::Meta::Method::Constructor::XS")); + + 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))); + void linearized_isa(SV* self) @@ -425,7 +623,7 @@ get_all_attributes(SV* self) PPCODE: { AV* const xc = mouse_get_xc(aTHX_ self); - AV* const all_attrs = MOUSE_xc_attrall(xc); + AV* const all_attrs = MOUSE_xc_attrall(xc); I32 const len = AvFILLp(all_attrs) + 1; I32 i; @@ -436,16 +634,17 @@ PPCODE: } SV* -new_object_(SV* meta, ...) +new_object(SV* meta, ...) CODE: { - HV* const args = mouse_build_args(aTHX_ meta, NULL, 1, items, ax); AV* const xc = mouse_get_xc(aTHX_ meta); + HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax, items); RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); mouse_class_initialize_object(aTHX_ meta, RETVAL, args, FALSE); } - +OUTPUT: + RETVAL void _initialize_object(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE) @@ -460,26 +659,139 @@ BOOT: INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles); INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id); + INSTALL_CLASS_HOLDER(Role, method_metaclass, "Mouse::Meta::Role::Method"); + MODULE = Mouse PACKAGE = Mouse::Object -HV* -BUILDARGS(SV* klass, ...) +SV* +new(SV* klass, ...) CODE: { - RETVAL = mouse_build_args(aTHX_ NULL, klass, 1, items, ax); + SV* const meta = mouse_initialize_metaclass(aTHX_ klass); + AV* const xc = mouse_get_xc(aTHX_ meta); + UV const flags = MOUSE_xc_flags(xc); + SV* args; + AV* buildall; + I32 len, i; + + /* BUILDARGS */ + if(flags & MOUSEf_XC_HAS_BUILDARGS){ + SPAGAIN; + + PUSHMARK(SP); + EXTEND(SP, items); + for(i = 0; i < items; i++){ + PUSHs(ST(i)); + } + //SP += items; + PUTBACK; + call_method("BUILDARGS", G_SCALAR); + SPAGAIN; + args = POPs; + PUTBACK; + + if(!IsHashRef(args)){ + croak("BUILDARGS did not return a HASH reference"); + } + } + else{ + args = newRV_inc((SV*)mouse_buildargs(aTHX_ meta, klass, ax, items)); + sv_2mortal(args); + } + + /* new_object */ + RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); + mouse_class_initialize_object(aTHX_ meta, RETVAL, (HV*)SvRV(args), FALSE); + + /* BUILDALL */ + buildall = MOUSE_xc_buildall(xc); + len = AvFILLp(buildall) + 1; + for(i = 0; i < len; i++){ + dSP; + + PUSHMARK(SP); + EXTEND(SP, 2); + PUSHs(RETVAL); /* self */ + PUSHs(args); + PUTBACK; + + call_sv(AvARRAY(buildall)[i], G_VOID | G_DISCARD); + } } OUTPUT: RETVAL -MODULE = Mouse PACKAGE = Mouse::Meta::Method::Constructor::XS +void +DESTROY(SV* object) +CODE: +{ + SV* const meta = get_metaclass(object); + AV* demolishall; + I32 len, i; + + if(!IsObject(object)){ + croak("You must not call DESTROY as a class method"); + } + + if(SvOK(meta)){ + AV* const xc = mouse_get_xc(aTHX_ meta); + + demolishall = MOUSE_xc_demolishall(xc); + } + else { + AV* const linearized_isa = mro_get_linear_isa(SvSTASH(SvRV(object))); + + len = AvFILLp(linearized_isa) + 1; -CV* -_generate_constructor(...) + demolishall = newAV_mortal(); + for(i = 0; i < len; i++){ + SV* const klass = MOUSE_av_at(linearized_isa, i); + HV* const st = gv_stashsv(klass, TRUE); + GV* const gv = stash_fetchs(st, "DEMOLISH", FALSE); + if(gv && GvCVu(gv)){ + av_push(demolishall, newRV_inc((SV*)GvCV(gv))); + } + } + } + + /* DEMOLISHALL */ + len = AvFILLp(demolishall) + 1; + if(len > 0){ + GV* const statusvalue = gv_fetchpvs("?", 0, SVt_PV); + SAVESPTR(GvSV(statusvalue)); /* local $? */ + SAVESPTR(ERRSV); /* local $@ */ + + GvSV(statusvalue) = sv_2mortal(newSViv(0)); + ERRSV = sv_2mortal(newSVpvs("")); + for(i = 0; i < len; i++){ + dSP; + + PUSHMARK(SP); + XPUSHs(object); + PUTBACK; + + call_sv(AvARRAY(demolishall)[i], G_VOID | G_DISCARD | G_EVAL); + if(SvTRUE(ERRSV)){ + SV* const e = newSVsv(ERRSV); + + FREETMPS; + LEAVE; + + sv_setsv(ERRSV, e); + SvREFCNT_dec(e); + croak(NULL); /* rethrow */ + } + } + } +} + +HV* +BUILDARGS(SV* klass, ...) CODE: { - RETVAL = get_cvs("Mouse::Object::new", TRUE); - SvREFCNT_inc_simple_void_NN(RETVAL); + RETVAL = mouse_buildargs(aTHX_ NULL, klass, ax, items); } OUTPUT: RETVAL +