use strict;
use warnings;
+use 5.008;
+
use MRO::Compat;
-use Carp 'confess';
-use Scalar::Util 'weaken';
+use Carp 'confess';
+use Scalar::Util 'weaken';
+
use Class::MOP::Class;
use Class::MOP::Attribute;
use Class::MOP::Immutable;
BEGIN {
- our $VERSION = '0.56';
- our $AUTHORITY = 'cpan:STEVAN';
-
- use XSLoader;
- XSLoader::load( 'Class::MOP', $VERSION );
-
*IS_RUNNING_ON_5_10 = ($] < 5.009_005)
? sub () { 0 }
- : sub () { 1 };
+ : sub () { 1 };
+
+ *HAVE_ISAREV = defined(&mro::get_isarev)
+ ? sub () { 1 }
+ : sub () { 1 };
+
+ # this is either part of core or set up appropriately by MRO::Compat
+ *check_package_cache_flag = \&mro::get_pkg_gen;
+}
+
+our $VERSION = '0.70_01';
+our $XS_VERSION = $VERSION;
+$VERSION = eval $VERSION;
+our $AUTHORITY = 'cpan:STEVAN';
- # get it from MRO::Compat now ...
- *check_package_cache_flag = \&mro::get_pkg_gen;
+# after that everything is loaded, if we're allowed try to load faster XS
+# versions of various things
+_try_load_xs() or _load_pure_perl();
+
+sub _try_load_xs {
+ return if $ENV{CLASS_MOP_NO_XS};
+
+ my $e = do {
+ local $@;
+ eval {
+ require XSLoader;
+ # just doing this - no warnings 'redefine' - doesn't work
+ # for some reason
+ local $^W = 0;
+ __PACKAGE__->XSLoader::load($XS_VERSION);
+
+ require Sub::Name;
+ Sub::Name->import(qw(subname));
+
+ require Devel::GlobalDestruction;
+ Devel::GlobalDestruction->import("in_global_destruction");
+ };
+ $@;
+ };
+
+ die $e if $e && $e !~ /object version|loadable object/;
+
+ return $e ? 0 : 1;
+}
+
+sub _load_pure_perl {
+ require Sub::Identify;
+ Sub::Identify->import('get_code_info');
+
+ *subname = sub { $_[1] };
+ *in_global_destruction = sub () { !1 }
}
+
{
# Metaclasses are singletons, so we cache them here.
# there is no need to worry about destruction though
# because I don't yet see a good reason to do so.
}
-sub load_class {
+sub load_first_existing_class {
+ my @classes = @_
+ or return;
+
+ foreach my $class (@classes) {
+ unless ( _is_valid_class_name($class) ) {
+ my $display = defined($class) ? $class : 'undef';
+ confess "Invalid class name ($display)";
+ }
+ }
+
+ my $found;
+ my %exceptions;
+ for my $class (@classes) {
+ my $e = _try_load_one_class($class);
+
+ if ($e) {
+ $exceptions{$class} = $e;
+ }
+ else {
+ $found = $class;
+ last;
+ }
+ }
+
+ return $found if $found;
+
+ confess join(
+ "\n",
+ map {
+ sprintf(
+ "Could not load class (%s) because : %s", $_,
+ $exceptions{$_}
+ )
+ } @classes
+ );
+}
+
+sub _try_load_one_class {
my $class = shift;
- # see if this is already
- # loaded in the symbol table
- return 1 if is_class_loaded($class);
- # otherwise require it ...
+
+ return if is_class_loaded($class);
+
my $file = $class . '.pm';
$file =~ s{::}{/}g;
- eval { CORE::require($file) };
- confess "Could not load class ($class) because : $@" if $@;
- unless (does_metaclass_exist($class)) {
- eval { Class::MOP::Class->initialize($class) };
- confess "Could not initialize class ($class) because : $@" if $@;
- }
- 1; # return true if it worked
+
+ return do {
+ local $@;
+ eval { require($file) };
+ $@;
+ };
+}
+
+sub load_class {
+ my $class = load_first_existing_class($_[0]);
+ return get_metaclass_by_name($class) || $class;
+}
+
+sub _is_valid_class_name {
+ my $class = shift;
+
+ return 0 if ref($class);
+ return 0 unless defined($class);
+ return 0 unless length($class);
+
+ return 1 if $class =~ /^\w+(?:::\w+)*$/;
+
+ return 0;
}
sub is_class_loaded {
my $class = shift;
- no strict 'refs';
- return 1 if defined ${"${class}::VERSION"} || defined @{"${class}::ISA"};
- foreach my $symbol (keys %{"${class}::"}) {
- next if substr($symbol, -2, 2) eq '::';
- return 1 if defined &{"${class}::${symbol}"};
+
+ return 0 if ref($class) || !defined($class) || !length($class);
+
+ # walk the symbol table tree to avoid autovififying
+ # \*{${main::}{"Foo::"}} == \*main::Foo::
+
+ my $pack = \*::;
+ foreach my $part (split('::', $class)) {
+ return 0 unless exists ${$$pack}{"${part}::"};
+ $pack = \*{${$$pack}{"${part}::"}};
}
+
+ # check for $VERSION or @ISA
+ return 1 if exists ${$$pack}{VERSION}
+ && defined *{${$$pack}{VERSION}}{SCALAR};
+ return 1 if exists ${$$pack}{ISA}
+ && defined *{${$$pack}{ISA}}{ARRAY};
+
+ # check for any method
+ foreach ( keys %{$$pack} ) {
+ next if substr($_, -2, 2) eq '::';
+
+ my $glob = ${$$pack}{$_} || next;
+
+ # constant subs
+ if ( IS_RUNNING_ON_5_10 ) {
+ return 1 if ref $glob eq 'SCALAR';
+ }
+
+ return 1 if defined *{$glob}{CODE};
+ }
+
+ # fail
return 0;
}
## Class::MOP::Package
Class::MOP::Package->meta->add_attribute(
- Class::MOP::Attribute->new('$!package' => (
+ Class::MOP::Attribute->new('package' => (
reader => {
# NOTE: we need to do this in order
# for the instance meta-object to
# rather than re-produce it here
'name' => \&Class::MOP::Package::name
},
- init_arg => 'package',
))
);
Class::MOP::Package->meta->add_attribute(
- Class::MOP::Attribute->new('%!namespace' => (
+ Class::MOP::Attribute->new('namespace' => (
reader => {
# NOTE:
# we just alias the original method
))
);
-# NOTE:
-# use the metaclass to construct the meta-package
-# which is a superclass of the metaclass itself :P
-Class::MOP::Package->meta->add_method('initialize' => sub {
- my $class = shift;
- my $package_name = shift;
- $class->meta->new_object('package' => $package_name, @_);
-});
-
## --------------------------------------------------------
## Class::MOP::Module
# the metaclass, isn't abstraction great :)
Class::MOP::Module->meta->add_attribute(
- Class::MOP::Attribute->new('$!version' => (
+ Class::MOP::Attribute->new('version' => (
reader => {
# NOTE:
# we just alias the original method
# well.
Class::MOP::Module->meta->add_attribute(
- Class::MOP::Attribute->new('$!authority' => (
+ Class::MOP::Attribute->new('authority' => (
reader => {
# NOTE:
# we just alias the original method
## Class::MOP::Class
Class::MOP::Class->meta->add_attribute(
- Class::MOP::Attribute->new('%!attributes' => (
+ Class::MOP::Attribute->new('attributes' => (
reader => {
# NOTE: we need to do this in order
# for the instance meta-object to
# rather than re-produce it here
'get_attribute_map' => \&Class::MOP::Class::get_attribute_map
},
- init_arg => 'attributes',
default => sub { {} }
))
);
Class::MOP::Class->meta->add_attribute(
- Class::MOP::Attribute->new('%!methods' => (
- init_arg => 'methods',
+ Class::MOP::Attribute->new('methods' => (
reader => {
# NOTE:
# we just alias the original method
);
Class::MOP::Class->meta->add_attribute(
- Class::MOP::Attribute->new('@!superclasses' => (
+ Class::MOP::Attribute->new('superclasses' => (
accessor => {
# NOTE:
# we just alias the original method
);
Class::MOP::Class->meta->add_attribute(
- Class::MOP::Attribute->new('$!attribute_metaclass' => (
+ Class::MOP::Attribute->new('attribute_metaclass' => (
reader => {
# NOTE:
# we just alias the original method
# rather than re-produce it here
'attribute_metaclass' => \&Class::MOP::Class::attribute_metaclass
},
- init_arg => 'attribute_metaclass',
default => 'Class::MOP::Attribute',
))
);
Class::MOP::Class->meta->add_attribute(
- Class::MOP::Attribute->new('$!method_metaclass' => (
+ Class::MOP::Attribute->new('method_metaclass' => (
reader => {
# NOTE:
# we just alias the original method
# rather than re-produce it here
'method_metaclass' => \&Class::MOP::Class::method_metaclass
},
- init_arg => 'method_metaclass',
default => 'Class::MOP::Method',
))
);
Class::MOP::Class->meta->add_attribute(
- Class::MOP::Attribute->new('$!instance_metaclass' => (
+ Class::MOP::Attribute->new('instance_metaclass' => (
reader => {
# NOTE: we need to do this in order
# for the instance meta-object to
# rather than re-produce it here
'instance_metaclass' => \&Class::MOP::Class::instance_metaclass
},
- init_arg => 'instance_metaclass',
default => 'Class::MOP::Instance',
))
);
## Class::MOP::Attribute
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!name' => (
- init_arg => 'name',
+ Class::MOP::Attribute->new('name' => (
reader => {
# NOTE: we need to do this in order
# for the instance meta-object to
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!associated_class' => (
- init_arg => 'associated_class',
+ Class::MOP::Attribute->new('associated_class' => (
reader => {
# NOTE: we need to do this in order
# for the instance meta-object to
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!accessor' => (
- init_arg => 'accessor',
+ Class::MOP::Attribute->new('accessor' => (
reader => { 'accessor' => \&Class::MOP::Attribute::accessor },
predicate => { 'has_accessor' => \&Class::MOP::Attribute::has_accessor },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!reader' => (
- init_arg => 'reader',
+ Class::MOP::Attribute->new('reader' => (
reader => { 'reader' => \&Class::MOP::Attribute::reader },
predicate => { 'has_reader' => \&Class::MOP::Attribute::has_reader },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!initializer' => (
- init_arg => 'initializer',
+ Class::MOP::Attribute->new('initializer' => (
reader => { 'initializer' => \&Class::MOP::Attribute::initializer },
predicate => { 'has_initializer' => \&Class::MOP::Attribute::has_initializer },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!writer' => (
- init_arg => 'writer',
+ Class::MOP::Attribute->new('writer' => (
reader => { 'writer' => \&Class::MOP::Attribute::writer },
predicate => { 'has_writer' => \&Class::MOP::Attribute::has_writer },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!predicate' => (
- init_arg => 'predicate',
+ Class::MOP::Attribute->new('predicate' => (
reader => { 'predicate' => \&Class::MOP::Attribute::predicate },
predicate => { 'has_predicate' => \&Class::MOP::Attribute::has_predicate },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!clearer' => (
- init_arg => 'clearer',
+ Class::MOP::Attribute->new('clearer' => (
reader => { 'clearer' => \&Class::MOP::Attribute::clearer },
predicate => { 'has_clearer' => \&Class::MOP::Attribute::has_clearer },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!builder' => (
- init_arg => 'builder',
+ Class::MOP::Attribute->new('builder' => (
reader => { 'builder' => \&Class::MOP::Attribute::builder },
predicate => { 'has_builder' => \&Class::MOP::Attribute::has_builder },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!init_arg' => (
- init_arg => 'init_arg',
+ Class::MOP::Attribute->new('init_arg' => (
reader => { 'init_arg' => \&Class::MOP::Attribute::init_arg },
predicate => { 'has_init_arg' => \&Class::MOP::Attribute::has_init_arg },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('$!default' => (
- init_arg => 'default',
+ Class::MOP::Attribute->new('default' => (
# default has a custom 'reader' method ...
predicate => { 'has_default' => \&Class::MOP::Attribute::has_default },
))
);
Class::MOP::Attribute->meta->add_attribute(
- Class::MOP::Attribute->new('@!associated_methods' => (
- init_arg => 'associated_methods',
+ Class::MOP::Attribute->new('associated_methods' => (
reader => { 'associated_methods' => \&Class::MOP::Attribute::associated_methods },
default => sub { [] }
))
);
-# NOTE: (meta-circularity)
-# This should be one of the last things done
-# it will "tie the knot" with Class::MOP::Attribute
-# so that it uses the attributes meta-objects
-# to construct itself.
-Class::MOP::Attribute->meta->add_method('new' => sub {
- my $class = shift;
- my $name = shift;
- my %options = @_;
-
- (defined $name && $name)
- || confess "You must provide a name for the attribute";
- $options{init_arg} = $name
- if not exists $options{init_arg};
-
- if(exists $options{builder}){
- confess("builder must be a defined scalar value which is a method name")
- if ref $options{builder} || !(defined $options{builder});
- confess("Setting both default and builder is not allowed.")
- if exists $options{default};
- } else {
- (Class::MOP::Attribute::is_default_a_coderef(\%options))
- || confess("References are not allowed as default values, you must ".
- "wrap then in a CODE reference (ex: sub { [] } and not [])")
- if exists $options{default} && ref $options{default};
- }
- # return the new object
- $class->meta->new_object(name => $name, %options);
-});
-
Class::MOP::Attribute->meta->add_method('clone' => sub {
my $self = shift;
$self->meta->clone_object($self, @_);
## --------------------------------------------------------
## Class::MOP::Method
-
Class::MOP::Method->meta->add_attribute(
- Class::MOP::Attribute->new('&!body' => (
- init_arg => 'body',
+ Class::MOP::Attribute->new('body' => (
reader => { 'body' => \&Class::MOP::Method::body },
))
);
+Class::MOP::Method->meta->add_attribute(
+ Class::MOP::Attribute->new('associated_metaclass' => (
+ reader => { 'associated_metaclass' => \&Class::MOP::Method::associated_metaclass },
+ ))
+);
+
+Class::MOP::Method->meta->add_attribute(
+ Class::MOP::Attribute->new('package_name' => (
+ reader => { 'package_name' => \&Class::MOP::Method::package_name },
+ ))
+);
+
+Class::MOP::Method->meta->add_attribute(
+ Class::MOP::Attribute->new('name' => (
+ reader => { 'name' => \&Class::MOP::Method::name },
+ ))
+);
+
+Class::MOP::Method->meta->add_attribute(
+ Class::MOP::Attribute->new('original_method' => (
+ reader => { 'original_method' => \&Class::MOP::Method::original_method },
+ writer => { '_set_original_method' => \&Class::MOP::Method::_set_original_method },
+ ))
+);
+
+Class::MOP::Method->meta->add_method('clone' => sub {
+ my $self = shift;
+ my $clone = $self->meta->clone_object($self, @_);
+ $clone->_set_original_method($self);
+ return $clone;
+});
+
## --------------------------------------------------------
## Class::MOP::Method::Wrapped
# practices of attributes, but we put
# it here for completeness
Class::MOP::Method::Wrapped->meta->add_attribute(
- Class::MOP::Attribute->new('%!modifier_table')
+ Class::MOP::Attribute->new('modifier_table')
);
## --------------------------------------------------------
## Class::MOP::Method::Generated
Class::MOP::Method::Generated->meta->add_attribute(
- Class::MOP::Attribute->new('$!is_inline' => (
- init_arg => 'is_inline',
+ Class::MOP::Attribute->new('is_inline' => (
reader => { 'is_inline' => \&Class::MOP::Method::Generated::is_inline },
+ default => 0,
))
);
## Class::MOP::Method::Accessor
Class::MOP::Method::Accessor->meta->add_attribute(
- Class::MOP::Attribute->new('$!attribute' => (
- init_arg => 'attribute',
+ Class::MOP::Attribute->new('attribute' => (
reader => {
'associated_attribute' => \&Class::MOP::Method::Accessor::associated_attribute
},
);
Class::MOP::Method::Accessor->meta->add_attribute(
- Class::MOP::Attribute->new('$!accessor_type' => (
- init_arg => 'accessor_type',
+ Class::MOP::Attribute->new('accessor_type' => (
reader => { 'accessor_type' => \&Class::MOP::Method::Accessor::accessor_type },
))
);
-
## --------------------------------------------------------
## Class::MOP::Method::Constructor
Class::MOP::Method::Constructor->meta->add_attribute(
- Class::MOP::Attribute->new('%!options' => (
- init_arg => 'options',
+ Class::MOP::Attribute->new('options' => (
reader => {
'options' => \&Class::MOP::Method::Constructor::options
},
+ default => sub { +{} }
))
);
Class::MOP::Method::Constructor->meta->add_attribute(
- Class::MOP::Attribute->new('$!associated_metaclass' => (
- init_arg => 'metaclass',
+ Class::MOP::Attribute->new('associated_metaclass' => (
+ init_arg => "metaclass", # FIXME alias and rename
reader => {
'associated_metaclass' => \&Class::MOP::Method::Constructor::associated_metaclass
},
# included for completeness
Class::MOP::Instance->meta->add_attribute(
- Class::MOP::Attribute->new('$!meta')
+ Class::MOP::Attribute->new('associated_metaclass',
+ reader => { associated_metaclass => \&Class::MOP::Instance::associated_metaclass },
+ ),
+);
+
+Class::MOP::Instance->meta->add_attribute(
+ Class::MOP::Attribute->new('_class_name',
+ init_arg => undef,
+ reader => { _class_name => \&Class::MOP::Instance::_class_name },
+ #lazy => 1, # not yet supported by Class::MOP but out our version does it anyway
+ #default => sub { $_[0]->associated_metaclass->name },
+ ),
);
Class::MOP::Instance->meta->add_attribute(
- Class::MOP::Attribute->new('@!slots')
+ Class::MOP::Attribute->new('attributes',
+ reader => { attributes => \&Class::MOP::Instance::get_all_attributes },
+ ),
);
+Class::MOP::Instance->meta->add_attribute(
+ Class::MOP::Attribute->new('slots',
+ reader => { slots => \&Class::MOP::Instance::slots },
+ ),
+);
+
+Class::MOP::Instance->meta->add_attribute(
+ Class::MOP::Attribute->new('slot_hash',
+ reader => { slot_hash => \&Class::MOP::Instance::slot_hash },
+ ),
+);
+
+
+# we need the meta instance of the meta instance to be created now, in order
+# for the constructor to be able to use it
+Class::MOP::Instance->meta->get_meta_instance;
+
+# pretend the add_method never happenned. it hasn't yet affected anything
+undef Class::MOP::Instance->meta->{_package_cache_flag};
+
## --------------------------------------------------------
## Now close all the Class::MOP::* classes
# no actual benefits.
$_->meta->make_immutable(
- inline_constructor => 0,
- inline_accessors => 0,
+ inline_constructor => 1,
+ replace_constructor => 1,
+ constructor_name => "_new",
+ inline_accessors => 0,
) for qw/
Class::MOP::Package
Class::MOP::Module
allows us to take advantage of new 5.10 features and stay backwards
compat.
+=item I<HAVE_ISAREV>
+
+Whether or not C<mro> provides C<get_isarev>, a much faster way to get all the
+subclasses of a certain class.
+
=back
=head2 Utility functions
+Note that these are all called as B<functions, not methods>.
+
=over 4
=item B<load_class ($class_name)>
=item B<check_package_cache_flag ($pkg)>
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
This will return an integer that is managed by C<Class::MOP::Class>
to determine if a module's symbol table has been altered.
=item B<get_code_info ($code)>
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
This function returns two values, the name of the package the C<$code>
is from and the name of the C<$code> itself. This is used by several
elements of the MOP to detemine where a given C<$code> reference is from.
+=item B<subname ($name, $code)>
+
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
+If possible, we will load the L<Sub::Name> module and this will function
+as C<Sub::Name::subname> does, otherwise it will just return the C<$code>
+argument.
+
+=item B<in_global_destruction>
+
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
+If L<Devel::GlobalDestruction> is available, this returns true under global
+destruction.
+
+Otherwise it's a constant returning false.
+
+=item B<load_first_existing_class ($class_name, [$class_name, ...])>
+
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
+Given a list of class names, this function will attempt to load each
+one in turn.
+
+If it finds a class it can load, it will return that class' name.
+If none of the classes can be loaded, it will throw an exception.
+
=back
=head2 Metaclass cache functions
Brandon (blblack) Black
+Florian (rafl) Ragwitz
+
Guillermo (groditi) Roditi
Matt (mst) Trout