sub method_metaclass() { 'Mouse::Meta::Method' }
sub attribute_metaclass() { 'Mouse::Meta::Attribute' }
-sub constructor_class() { 'Mouse::Meta::Method::Constructor' }
+sub constructor_class(); # XS
sub destructor_class() { 'Mouse::Meta::Method::Destructor' }
sub _construct_meta {
return $object;
}
-sub _initialize_object{
- my($self, $object, $args, $ignore_triggers) = @_;
-
- my @triggers_queue;
-
- foreach my $attribute ($self->get_all_attributes) {
- my $from = $attribute->init_arg;
- my $key = $attribute->name;
-
- if (defined($from) && exists($args->{$from})) {
- $object->{$key} = $attribute->_coerce_and_verify($args->{$from}, $object);
-
- weaken($object->{$key})
- if ref($object->{$key}) && $attribute->is_weak_ref;
-
- if ($attribute->has_trigger) {
- push @triggers_queue, [ $attribute->trigger, $object->{$key} ];
- }
- }
- else {
- if ($attribute->has_default || $attribute->has_builder) {
- unless ($attribute->is_lazy) {
- my $default = $attribute->default;
- my $builder = $attribute->builder;
- my $value = $builder ? $object->$builder()
- : ref($default) eq 'CODE' ? $object->$default()
- : $default;
-
- $object->{$key} = $attribute->_coerce_and_verify($value, $object);
-
- weaken($object->{$key})
- if ref($object->{$key}) && $attribute->is_weak_ref;
- }
- }
- else {
- if ($attribute->is_required) {
- $self->throw_error("Attribute (".$attribute->name.") is required");
- }
- }
- }
- }
-
- if(!$ignore_triggers){
- foreach my $trigger_and_value(@triggers_queue){
- my($trigger, $value) = @{$trigger_and_value};
- $trigger->($object, $value);
- }
- }
-
- if($self->is_anon_class){
- $object->{__METACLASS__} = $self;
- }
-
- return $object;
-}
-
sub clone_object {
my $class = shift;
my $object = shift;
package
Mouse::Meta::Class;
+sub constructor_class() { 'Mouse::Meta::Method::Constructor' }
+
sub is_anon_class{
return exists $_[0]->{anon_serial_id};
}
return values %attrs;
}
+sub _initialize_object{
+ my($self, $object, $args, $ignore_triggers) = @_;
+
+ my @triggers_queue;
+
+ foreach my $attribute ($self->get_all_attributes) {
+ my $init_arg = $attribute->init_arg;
+ my $slot = $attribute->name;
+
+ if (defined($init_arg) && exists($args->{$init_arg})) {
+ $object->{$slot} = $attribute->_coerce_and_verify($args->{$init_arg}, $object);
+
+ weaken($object->{$slot})
+ if ref($object->{$slot}) && $attribute->is_weak_ref;
+
+ if ($attribute->has_trigger) {
+ push @triggers_queue, [ $attribute->trigger, $object->{$slot} ];
+ }
+ }
+ else { # no init arg
+ if ($attribute->has_default || $attribute->has_builder) {
+ if (!$attribute->is_lazy) {
+ my $default = $attribute->default;
+ my $builder = $attribute->builder;
+ my $value = $builder ? $object->$builder()
+ : ref($default) eq 'CODE' ? $object->$default()
+ : $default;
+
+ $object->{$slot} = $attribute->_coerce_and_verify($value, $object);
+
+ weaken($object->{$slot})
+ if ref($object->{$slot}) && $attribute->is_weak_ref;
+ }
+ }
+ elsif($attribute->is_required) {
+ $self->throw_error("Attribute (".$attribute->name.") is required");
+ }
+ }
+ }
+
+ if(!$ignore_triggers){
+ foreach my $trigger_and_value(@triggers_queue){
+ my($trigger, $value) = @{$trigger_and_value};
+ $trigger->($object, $value);
+ }
+ }
+
+ if($self->is_anon_class){
+ $object->{__METACLASS__} = $self;
+ }
+
+ return;
+}
+
+
package
Mouse::Meta::Role;
/* Mouse XS Attribute object */
AV* mouse_get_xa(pTHX_ SV* const attr);
+SV* mouse_xa_apply_type_constraint(pTHX_ AV* const xa, SV* value, U16 const flags);
+SV* mouse_xa_set_default(pTHX_ AV* const xa, SV* const object);
enum mouse_xa_ix_t{
MOUSE_XA_SLOT, /* for constructors, sync to mg_obj */
MOUSE_XA_FLAGS, /* for constructors, sync to mg_private */
MOUSE_XA_ATTRIBUTE,
+ MOUSE_XA_INIT_ARG,
MOUSE_XA_TC,
MOUSE_XA_TC_CODE,
};
#define MOUSE_xa_slot(m) MOUSE_av_at(m, MOUSE_XA_SLOT)
-#define MOUSE_xa_flags(m) MOUSE_av_at(m, MOUSE_XA_FLAGS)
+#define MOUSE_xa_flags(m) SvUVX( MOUSE_av_at(m, MOUSE_XA_FLAGS) )
#define MOUSE_xa_attribute(m) MOUSE_av_at(m, MOUSE_XA_ATTRIBUTE)
+#define MOUSE_xa_init_arg(m) MOUSE_av_at(m, MOUSE_XA_INIT_ARG)
#define MOUSE_xa_tc(m) MOUSE_av_at(m, MOUSE_XA_TC)
#define MOUSE_xa_tc_code(m) MOUSE_av_at(m, MOUSE_XA_TC_CODE)
-
enum mouse_xa_flags_t{
MOUSEf_ATTR_HAS_TC = 0x0001,
MOUSEf_ATTR_HAS_DEFAULT = 0x0002,
MOUSEf_ATTR_HAS_BUILDER = 0x0004,
- MOUSEf_ATTR_HAS_INITIALIZER = 0x0008,
+ MOUSEf_ATTR_HAS_INITIALIZER = 0x0008, /* not used */
MOUSEf_ATTR_HAS_TRIGGER = 0x0010,
MOUSEf_ATTR_IS_LAZY = 0x0020,
SV* mouse_get_attribute;
SV* mouse_get_attribute_list;
-
+#define MOUSE_xc_flags(a) SvUVX(MOUSE_av_at((a), MOUSE_XC_FLAGS))
#define MOUSE_xc_gen(a) MOUSE_av_at((a), MOUSE_XC_GEN)
#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) )
/* Mouse XS Metaclass object */
enum mouse_xc_ix_t{
+ MOUSE_XC_FLAGS,
+
MOUSE_XC_GEN, /* class generation */
MOUSE_XC_STASH, /* symbol table hash */
- MOUSE_XC_FLAGS,
MOUSE_XC_BUILDARGS, /* Custom BUILDARGS */
/* update */
- if(SvTRUEx( mcall0s(metaclass, "is_immutable") )){
+ if(predicate_calls(metaclass, "is_immutable")){
flags |= MOUSEf_XC_IS_IMMUTABLE;
}
+ if(predicate_calls(metaclass, "is_anon_class")){
+ flags |= MOUSEf_XC_IS_ANON;
+ }
+
if(mouse_class_has_custom_buildargs(aTHX_ stash)){
flags |= MOUSEf_XC_HAS_BUILDARGS;
}
+ av_store(xc, MOUSE_XC_FLAGS, newSVuv(flags));
av_store(xc, MOUSE_XC_ATTRALL, (SV*)attrall);
av_store(xc, MOUSE_XC_BUILDALL, (SV*)buildall);
av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall);
av_store(xc, MOUSE_XC_GEN, newSViv(0));
av_store(xc, MOUSE_XC_STASH, (SV*)stash);
+
SvREFCNT_inc_simple_void_NN(stash);
}
else{
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);
- I32 const len = AvFILLp(attrs) + 1;
+ I32 len = AvFILLp(attrs) + 1;
I32 i;
- AV* const triggers_queue = (ignore_triggers ? NULL : newAV_mortal());
+ AV* triggers_queue = NULL;
+
+ ENTER;
+ SAVETMPS;
+
+ if(!ignore_triggers){
+ triggers_queue = newAV_mortal();
+ }
for(i = 0; i < len; i++){
- AV* const xa = mouse_get_xa(aTHX_ AvARRAY(attrs)[i]);
+ SV* const attr = AvARRAY(attrs)[i];
+ AV* const xa = mouse_get_xa(aTHX_ AvARRAY(attrs)[i]);
+
+ SV* const slot = MOUSE_xa_slot(xa);
+ U16 const flags = (U16)MOUSE_xa_flags(xa);
+ SV* const init_arg = MOUSE_xa_init_arg(xa);
+ HE* he;
+
+ if(SvOK(init_arg) && ( he = hv_fetch_ent(args, init_arg, FALSE, 0U) ) ){
+ SV* value = HeVAL(he);
+ if(flags & MOUSEf_ATTR_HAS_TC){
+ value = mouse_xa_apply_type_constraint(aTHX_ xa, value, flags);
+ }
+ set_slot(object, slot, value);
+ if(SvROK(value) && flags & MOUSEf_ATTR_IS_WEAK_REF){
+ weaken_slot(object, slot);
+ }
+ if(flags & MOUSEf_ATTR_HAS_TRIGGER && triggers_queue){
+ AV* const pair = newAV();
+ av_push(pair, newSVsv( mcall0s(attr, "trigger") ));
+ av_push(pair, newSVsv(value));
+
+ av_push(triggers_queue, (SV*)pair);
+ }
+ }
+ else { /* no init arg */
+ if(flags & (MOUSEf_ATTR_HAS_DEFAULT | MOUSEf_ATTR_HAS_BUILDER)){
+ if(!(flags & MOUSEf_ATTR_IS_LAZY)){
+ mouse_xa_set_default(aTHX_ xa, object);
+ }
+ }
+ else if(flags & MOUSEf_ATTR_IS_REQUIRED) {
+ mouse_throw_error(attr, NULL, "Attribute (%"SVf") is required", slot);
+ }
+ }
+ } /* for each attributes */
+
+ if(triggers_queue){
+ 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];
+ SV* const value = AvARRAY(pair)[1];
+
+ mcall1(object, trigger, value);
+ }
+ }
+
+ if(MOUSE_xc_flags(xc) & MOUSEf_XC_IS_ANON){
+ set_slot(object, newSVpvs_flags("__ANON__", SVs_TEMP), meta);
}
+
+ FREETMPS;
+ LEAVE;
}
MODULE = Mouse PACKAGE = Mouse
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"));
void
linearized_isa(SV* self)
void
-_initialize_object_(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE)
+_initialize_object(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE)
CODE:
{
mouse_class_initialize_object(aTHX_ meta, object, args, ignore_triggers);
OUTPUT:
RETVAL
+MODULE = Mouse PACKAGE = Mouse::Meta::Method::Constructor::XS
+
+CV*
+_generate_constructor(...)
+CODE:
+{
+ RETVAL = get_cvs("Mouse::Object::new", TRUE);
+ SvREFCNT_inc_simple_void_NN(RETVAL);
+}
+OUTPUT:
+ RETVAL
+
mg = sv_magicext((SV*)xsub, MOUSE_xa_slot(xa), PERL_MAGIC_ext, &mouse_accessor_vtbl, (char*)xa, HEf_SVKEY);
- MOUSE_mg_flags(mg) = (U16)SvUV(MOUSE_xa_flags(xa));
+ MOUSE_mg_flags(mg) = (U16)MOUSE_xa_flags(xa);
/* NOTE:
* although we use MAGIC for gc, we also store mg to CvXSUBANY for efficiency (gfx)
return xsub;
}
-static SV*
-mouse_apply_type_constraint(pTHX_ AV* const xa, SV* value, U16 const flags){
- SV* const tc = MOUSE_xa_tc(xa);
- SV* tc_code;
-
- if(flags & MOUSEf_ATTR_SHOULD_COERCE){
- value = mcall1s(tc, "coerce", value);
- }
-
- if(!SvOK(MOUSE_xa_tc_code(xa))){
- tc_code = mcall0s(tc, "_compiled_type_constraint");
- av_store(xa, MOUSE_XA_TC_CODE, newSVsv(tc_code));
-
- if(!IsCodeRef(tc_code)){
- mouse_throw_error(MOUSE_xa_attribute(xa), tc, "Not a CODE reference");
- }
- }
- else{
- tc_code = MOUSE_xa_tc_code(xa);
- }
-
- if(!mouse_tc_check(aTHX_ tc_code, value)){
- mouse_throw_error(MOUSE_xa_attribute(xa), value,
- "Attribute (%"SVf") does not pass the type constraint because: %"SVf,
- mcall0(MOUSE_xa_attribute(xa), mouse_name),
- mcall1s(tc, "get_message", value));
- }
-
- return value;
-}
#define PUSH_VALUE(value, flags) STMT_START { \
if((flags) & MOUSEf_ATTR_SHOULD_AUTO_DEREF && GIMME_V == G_ARRAY){ \
static void
mouse_attr_get(pTHX_ SV* const self, MAGIC* const mg){
U16 const flags = MOUSE_mg_flags(mg);
- SV* const slot = MOUSE_mg_slot(mg);
SV* value;
- value = get_slot(self, slot);
+ value = get_slot(self, MOUSE_mg_slot(mg));
/* check_lazy */
if( !value && flags & MOUSEf_ATTR_IS_LAZY ){
- AV* const xa = MOUSE_mg_xa(mg);
- SV* const attr = MOUSE_xa_attribute(xa);
-
- /* get default value by $attr->builder or $attr->default */
- if(flags & MOUSEf_ATTR_HAS_BUILDER){
- SV* const builder = mcall0s(attr, "builder");
- value = mcall0(self, builder);
- }
- else {
- value = mcall0s(attr, "default");
-
- if(IsCodeRef(value)){
- value = mcall0(self, value);
- }
- }
-
- /* apply coerce and type constraint */
- if(flags & MOUSEf_ATTR_HAS_TC){
- value = mouse_apply_type_constraint(aTHX_ xa, value, flags);
- }
-
- /* store value to slot */
- value = set_slot(self, slot, value);
+ value = mouse_xa_set_default(aTHX_ MOUSE_mg_xa(mg), self);
}
PUSH_VALUE(value, flags);
SV* const slot = MOUSE_mg_slot(mg);
if(flags & MOUSEf_ATTR_HAS_TC){
- value = mouse_apply_type_constraint(aTHX_ MOUSE_mg_xa(mg), value, flags);
+ value = mouse_xa_apply_type_constraint(aTHX_ MOUSE_mg_xa(mg), value, flags);
}
set_slot(self, slot, value);
av_store(xa, MOUSE_XA_ATTRIBUTE, newSVsv(attr));
+ av_store(xa, MOUSE_XA_INIT_ARG, newSVsv(mcall0s(attr, "init_arg")));
+
if(predicate_calls(attr, "has_type_constraint")){
SV* tc;
flags |= MOUSEf_ATTR_HAS_TC;
if(predicate_calls(attr, "is_lazy")){
flags |= MOUSEf_ATTR_IS_LAZY;
-
- if(predicate_calls(attr, "has_builder")){
- flags |= MOUSEf_ATTR_HAS_BUILDER;
- }
- else if(predicate_calls(attr, "has_default")){
- flags |= MOUSEf_ATTR_HAS_DEFAULT;
- }
+ }
+ if(predicate_calls(attr, "has_builder")){
+ flags |= MOUSEf_ATTR_HAS_BUILDER;
+ }
+ else if(predicate_calls(attr, "has_default")){
+ flags |= MOUSEf_ATTR_HAS_DEFAULT;
}
if(predicate_calls(attr, "is_weak_ref")){
return xa;
}
+SV*
+mouse_xa_apply_type_constraint(pTHX_ AV* const xa, SV* value, U16 const flags){
+ SV* const tc = MOUSE_xa_tc(xa);
+ SV* tc_code;
+
+ if(flags & MOUSEf_ATTR_SHOULD_COERCE){
+ value = mcall1s(tc, "coerce", value);
+ }
+
+ if(!SvOK(MOUSE_xa_tc_code(xa))){
+ tc_code = mcall0s(tc, "_compiled_type_constraint");
+ av_store(xa, MOUSE_XA_TC_CODE, newSVsv(tc_code));
+
+ if(!IsCodeRef(tc_code)){
+ mouse_throw_error(MOUSE_xa_attribute(xa), tc, "Not a CODE reference");
+ }
+ }
+ else{
+ tc_code = MOUSE_xa_tc_code(xa);
+ }
+
+ if(!mouse_tc_check(aTHX_ tc_code, value)){
+ mouse_throw_error(MOUSE_xa_attribute(xa), value,
+ "Attribute (%"SVf") does not pass the type constraint because: %"SVf,
+ mcall0(MOUSE_xa_attribute(xa), mouse_name),
+ mcall1s(tc, "get_message", value));
+ }
+
+ return value;
+}
+
+
+SV*
+mouse_xa_set_default(pTHX_ AV* const xa, SV* const object) {
+ U16 const flags = (U16)MOUSE_xa_flags(xa);
+ SV* value;
+
+ /* get default value by $attr->builder or $attr->default */
+ if(flags & MOUSEf_ATTR_HAS_BUILDER){
+ SV* const builder = mcall0s(MOUSE_xa_attribute(xa), "builder");
+ value = mcall0(object, builder); /* $object->$builder() */
+ }
+ else {
+ value = mcall0s(MOUSE_xa_attribute(xa), "default");
+
+ if(IsCodeRef(value)){
+ value = mcall0(object, value);
+ }
+ }
+
+ /* apply coerce and type constraint */
+ if(flags & MOUSEf_ATTR_HAS_TC){
+ value = mouse_xa_apply_type_constraint(aTHX_ xa, value, flags);
+ }
+
+ /* store value to slot */
+ value = set_slot(object, MOUSE_xa_slot(xa), value);
+
+ if(flags & MOUSEf_ATTR_IS_WEAK_REF && SvROK(value)){
+ weaken_slot(object, MOUSE_xa_slot(xa));
+ }
+
+ return value;
+}
MODULE = Mouse::Meta::Attribute PACKAGE = Mouse::Meta::Attribute