use Try::Tiny;
use List::MoreUtils 'all';
-our $VERSION = '1.09';
+our $VERSION = '1.10';
$VERSION = eval $VERSION;
our $AUTHORITY = 'cpan:STEVAN';
my $old_metaclass = blessed($options{package})
? $options{package}
: Class::MOP::get_metaclass_by_name($options{package});
+ $options{weaken} = Class::MOP::metaclass_is_weak($old_metaclass->name)
+ if !exists $options{weaken}
+ && blessed($old_metaclass)
+ && $old_metaclass->isa('Class::MOP::Class');
$old_metaclass->_remove_generated_metaobjects
if $old_metaclass && $old_metaclass->isa('Class::MOP::Class');
- my $new_metaclass = $class->SUPER::reinitialize(@args);
+ my $new_metaclass = $class->SUPER::reinitialize(%options);
$new_metaclass->_restore_metaobjects_from($old_metaclass)
if $old_metaclass && $old_metaclass->isa('Class::MOP::Class');
return $new_metaclass;
# NOTE:
# we need to weaken any anon classes
# so that they can call DESTROY properly
- Class::MOP::weaken_metaclass($package_name) if $meta->is_anon_class;
+ Class::MOP::weaken_metaclass($package_name) if $options->{weaken};
$meta;
}
my $super_meta = Class::MOP::get_metaclass_by_name($superclass_name)
|| return 1;
- my $super_meta_type = $super_meta->_real_ref_name;
+ my $super_meta_name = $super_meta->_real_ref_name;
- return $self->isa($super_meta_type);
+ return $self->_is_compatible_with($super_meta_name);
}
sub _check_single_metaclass_compatibility {
# this is a really odd case
return 0 unless defined $self->$metaclass_type;
- return $self->$metaclass_type->isa($super_meta->$metaclass_type);
+ return $self->$metaclass_type->_is_compatible_with($super_meta->$metaclass_type);
}
sub _fix_metaclass_incompatibility {
sub _can_fix_metaclass_incompatibility {
my $self = shift;
- return $self->_can_fix_metaclass_incompatibility_by_subclassing(@_);
-}
-
-sub _can_fix_metaclass_incompatibility_by_subclassing {
- my $self = shift;
my ($super_meta) = @_;
- return 1 if $self->_can_fix_class_metaclass_incompatibility_by_subclassing($super_meta);
+ return 1 if $self->_class_metaclass_can_be_made_compatible($super_meta);
my %base_metaclass = $self->_base_metaclasses;
for my $metaclass_type (keys %base_metaclass) {
- return 1 if $self->_can_fix_single_metaclass_incompatibility_by_subclassing($metaclass_type, $super_meta);
+ return 1 if $self->_single_metaclass_can_be_made_compatible($super_meta, $metaclass_type);
}
return;
}
-sub _can_fix_class_metaclass_incompatibility_by_subclassing {
+sub _class_metaclass_can_be_made_compatible {
my $self = shift;
my ($super_meta) = @_;
- my $super_meta_type = $super_meta->_real_ref_name;
-
- return $super_meta_type ne blessed($self)
- && $super_meta->isa(blessed($self));
+ return $self->_can_be_made_compatible_with($super_meta->_real_ref_name);
}
-sub _can_fix_single_metaclass_incompatibility_by_subclassing {
+sub _single_metaclass_can_be_made_compatible {
my $self = shift;
- my ($metaclass_type, $super_meta) = @_;
+ my ($super_meta, $metaclass_type) = @_;
my $specific_meta = $self->$metaclass_type;
+
return unless $super_meta->can($metaclass_type);
my $super_specific_meta = $super_meta->$metaclass_type;
# this is a really odd case
return 1 unless defined $specific_meta;
- return $specific_meta ne $super_specific_meta
- && $super_specific_meta->isa($specific_meta);
+ return 1 if $specific_meta->_can_be_made_compatible_with($super_specific_meta);
}
sub _fix_class_metaclass_incompatibility {
my $self = shift;
my ( $super_meta ) = @_;
- if ($self->_can_fix_class_metaclass_incompatibility_by_subclassing($super_meta)) {
+ if ($self->_class_metaclass_can_be_made_compatible($super_meta)) {
($self->is_pristine)
|| confess "Can't fix metaclass incompatibility for "
. $self->name
my $super_meta_name = $super_meta->_real_ref_name;
- $super_meta_name->meta->rebless_instance($self);
+ $self->_make_compatible_with($super_meta_name);
}
}
my $self = shift;
my ( $metaclass_type, $super_meta ) = @_;
- if ($self->_can_fix_single_metaclass_incompatibility_by_subclassing($metaclass_type, $super_meta)) {
+ if ($self->_single_metaclass_can_be_made_compatible($super_meta, $metaclass_type)) {
($self->is_pristine)
|| confess "Can't fix metaclass incompatibility for "
. $self->name
. " because it is not pristine.";
- $self->{$metaclass_type} = $super_meta->$metaclass_type;
+ my $new_metaclass = $self->$metaclass_type
+ ? $self->$metaclass_type->_get_compatible_metaclass($super_meta->$metaclass_type)
+ : $super_meta->$metaclass_type;
+ $self->{$metaclass_type} = $new_metaclass;
}
}
my $self = shift;
my ($old_meta) = @_;
- for my $method ($old_meta->_get_local_methods) {
- $self->_make_metaobject_compatible($method);
- $self->add_method($method->name => $method);
- }
-
- for my $attr (sort { $a->insertion_order <=> $b->insertion_order }
- map { $old_meta->get_attribute($_) }
- $old_meta->get_attribute_list) {
- $self->_make_metaobject_compatible($attr);
- $self->add_attribute($attr);
- }
+ $self->_restore_metamethods_from($old_meta);
+ $self->_restore_metaattributes_from($old_meta);
}
sub _remove_generated_metaobjects {
}
}
-sub _make_metaobject_compatible {
- my $self = shift;
- my ($object) = @_;
-
- my $new_metaclass = $self->_get_compatible_single_metaclass(blessed($object));
-
- if (!defined($new_metaclass)) {
- confess "Can't make $object compatible with metaclass "
- . $self->_get_associated_single_metaclass(blessed($object));
- }
-
- # can't use rebless_instance here, because it might not be an actual
- # subclass in the case of, e.g. moose role reconciliation
- $new_metaclass->meta->_force_rebless_instance($object)
- if blessed($object) ne $new_metaclass;
-
- return $object;
-}
-
-sub _get_associated_single_metaclass {
- my $self = shift;
- my ($single_meta_name) = @_;
-
- my $current_single_meta_name;
- if ($single_meta_name->isa('Class::MOP::Method')) {
- $current_single_meta_name = $self->method_metaclass;
- }
- elsif ($single_meta_name->isa('Class::MOP::Attribute')) {
- $current_single_meta_name = $self->attribute_metaclass;
- }
- else {
- confess "Can't make $single_meta_name compatible, it isn't an "
- . "attribute or method metaclass.";
- }
-
- return $current_single_meta_name;
-}
-
-sub _get_compatible_single_metaclass {
- my $self = shift;
- my ($single_meta_name) = @_;
-
- return $self->_get_compatible_single_metaclass_by_subclassing($single_meta_name);
-}
-
-sub _get_compatible_single_metaclass_by_subclassing {
- my $self = shift;
- my ($single_meta_name) = @_;
-
- my $current_single_meta_name = $self->_get_associated_single_metaclass($single_meta_name);
-
- if ($single_meta_name->isa($current_single_meta_name)) {
- return $single_meta_name;
- }
- elsif ($current_single_meta_name->isa($single_meta_name)) {
- return $current_single_meta_name;
- }
-
- return;
-}
-
## ANON classes
{
sub create_anon_class {
my ($class, %options) = @_;
+ $options{weaken} = 1 unless exists $options{weaken};
my $package_name = $ANON_CLASS_PREFIX . ++$ANON_CLASS_SERIAL;
return $class->create($package_name, %options);
}
|| confess "You must pass a HASH ref of methods"
if exists $options{methods};
+ $options{meta_name} = 'meta'
+ unless exists $options{meta_name};
+
my (%initialize_options) = @args;
delete @initialize_options{qw(
package
superclasses
attributes
methods
- no_meta
+ meta_name
version
authority
)};
$meta->_instantiate_module( $options{version}, $options{authority} );
- # FIXME totally lame
- $meta->add_method('meta' => sub {
- if (Class::MOP::DEBUG_NO_META()) {
- my ($self) = @_;
- if (my $meta = try { $self->SUPER::meta }) {
- return $meta if $meta->isa('Class::MOP::Class');
- }
- confess "'meta' method called by MOP internals"
- if caller =~ /Class::MOP|metaclass/;
- }
- $class->initialize(ref($_[0]) || $_[0]);
- }) unless $options{no_meta};
+ $meta->_add_meta_method($options{meta_name})
+ if defined $options{meta_name};
$meta->superclasses(@{$options{superclasses}})
if exists $options{superclasses};
foreach my $attr ($class->get_all_attributes()) {
$attr->initialize_instance_slot($meta_instance, $instance, $params);
}
- # NOTE:
- # this will only work for a HASH instance type
- if ($class->is_anon_class) {
- (reftype($instance) eq 'HASH')
- || confess "Currently only HASH based instances are supported with instance of anon-classes";
- # NOTE:
- # At some point we should make this official
- # as a reserved slot name, but right now I am
- # going to keep it here.
- # my $RESERVED_MOP_SLOT = '__MOP__';
- $instance->{'__MOP__'} = $class;
+ if (Class::MOP::metaclass_is_weak($class->name)) {
+ $meta_instance->_set_mop_slot($instance, $class);
}
return $instance;
}
return $self->get_meta_instance->inline_rebless_instance_structure(@_);
}
+sub _inline_get_mop_slot {
+ my $self = shift;
+
+ return $self->get_meta_instance->_inline_get_mop_slot(@_);
+}
+
+sub _inline_set_mop_slot {
+ my $self = shift;
+
+ return $self->get_meta_instance->_inline_set_mop_slot(@_);
+}
+
+sub _inline_clear_mop_slot {
+ my $self = shift;
+
+ return $self->get_meta_instance->_inline_clear_mop_slot(@_);
+}
+
sub clone_object {
my $class = shift;
my $instance = shift;
my $meta_instance = $self->get_meta_instance;
+ if (Class::MOP::metaclass_is_weak($old_metaclass->name)) {
+ $meta_instance->_clear_mop_slot($instance);
+ }
+
# rebless!
# we use $_[1] here because of t/306_rebless_overload.t regressions on 5.8.8
$meta_instance->rebless_instance_structure($_[1], $self);
$self->_fixup_attributes_after_rebless($instance, $old_metaclass, %params);
+
+ if (Class::MOP::metaclass_is_weak($self->name)) {
+ $meta_instance->_set_mop_slot($instance, $self);
+ }
}
sub rebless_instance {
sub _superclasses_updated {
my $self = shift;
$self->update_meta_instance_dependencies();
+ # keep strong references to all our parents, so they don't disappear if
+ # they are anon classes and don't have any direct instances
+ $self->_superclass_metas(
+ map { Class::MOP::class_of($_) } $self->superclasses
+ );
+}
+
+sub _superclass_metas {
+ my $self = shift;
+ $self->{_superclass_metas} = [@_];
}
sub subclasses {
An optional array reference of L<Class::MOP::Attribute> objects.
-=item * no_meta
+=item * meta_name
-If true, a C<meta> method will not be installed into the class.
+Specifies the name to install the C<meta> method for this class under.
+If it is not passed, C<meta> is assumed, and if C<undef> is explicitly
+given, no meta method will be installed.
+
+=item * weaken
+
+If true, the metaclass that is stored in the global cache will be a
+weak reference.
+
+Classes created in this way are destroyed once the metaclass they are
+attached to goes out of scope, and will be removed from Perl's internal
+symbol table.
+
+All instances of a class with a weakened metaclass keep a special
+reference to the metaclass object, which prevents the metaclass from
+going out of scope while any instances exist.
+
+This only works if the instance is based on a hash reference, however.
=back
It accepts the same C<superclasses>, C<methods>, and C<attributes>
parameters that C<create> accepts.
-Anonymous classes are destroyed once the metaclass they are attached
-to goes out of scope, and will be removed from Perl's internal symbol
-table.
-
-All instances of an anonymous class keep a special reference to the
-metaclass object, which prevents the metaclass from going out of scope
-while any instances exist.
-
-This only works if the instance is based on a hash reference, however.
+Anonymous classes default to C<< weaken => 1 >>, although this can be
+overridden.
=item B<< Class::MOP::Class->initialize($package_name, %options) >>