X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FClass%2FMOP.pm;h=c1f76427b457887bd3462769df6910f484c927e4;hb=94278c1ba6283af20c09a6aef615954825d08162;hp=712895479ab31806b3a312d9a0685cf00e07ad65;hpb=11b56828515cd0762ef7cbd01a9ea543602ff19e;p=gitmo%2FClass-MOP.git diff --git a/lib/Class/MOP.pm b/lib/Class/MOP.pm index 7128954..c1f7642 100644 --- a/lib/Class/MOP.pm +++ b/lib/Class/MOP.pm @@ -4,10 +4,31 @@ package Class::MOP; use strict; use warnings; +use 5.008; + use MRO::Compat; -use Carp 'confess'; -use Scalar::Util 'weaken'; +use Carp 'confess'; +use Scalar::Util 'weaken'; + +BEGIN { + local $@; + eval { + require Sub::Name; + Sub::Name->import(qw(subname)); + 1 + } or eval 'sub subname { $_[1] }'; + + # this is either part of core or set up appropriately by MRO::Compat + *check_package_cache_flag = \&mro::get_pkg_gen; + + eval { + require Devel::GlobalDestruction; + Devel::GlobalDestruction->import("in_global_destruction"); + 1; + } or *in_global_destruction = sub () { !1 }; +} + use Class::MOP::Class; use Class::MOP::Attribute; @@ -16,20 +37,50 @@ use Class::MOP::Method; 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 }; +} + +our $VERSION = '0.65'; +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); + }; + $@; + }; + + 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'); +} + + { # Metaclasses are singletons, so we cache them here. # there is no need to worry about destruction though @@ -57,29 +108,75 @@ BEGIN { sub load_class { my $class = shift; - # see if this is already - # loaded in the symbol table - return 1 if is_class_loaded($class); - # otherwise require it ... - my $file = $class . '.pm'; - $file =~ s{::}{/}g; - eval { CORE::require($file) }; - confess "Could not load class ($class) because : $@" if $@; + + unless ( _is_valid_class_name($class) ) { + my $display = defined($class) ? $class : 'undef'; + confess "Invalid class name ($display)"; + } + + # if the class is not already loaded in the symbol table.. + unless (is_class_loaded($class)) { + # require it + my $e = do { local $@; eval "require $class"; $@ }; + confess "Could not load class ($class) because : $e" if $e; + } + + # initialize a metaclass if necessary unless (does_metaclass_exist($class)) { - eval { Class::MOP::Class->initialize($class) }; - confess "Could not initialize class ($class) because : $@" if $@; + my $e = do { local $@; eval { Class::MOP::Class->initialize($class) }; $@ }; + confess "Could not initialize class ($class) because : $e" if $e; } - 1; # return true if it worked + + return get_metaclass_by_name($class) if defined wantarray; +} + +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; } @@ -112,7 +209,7 @@ sub is_class_loaded { ## 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 @@ -122,12 +219,11 @@ Class::MOP::Package->meta->add_attribute( # 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 @@ -139,15 +235,6 @@ Class::MOP::Package->meta->add_attribute( )) ); -# 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 @@ -162,7 +249,7 @@ Class::MOP::Package->meta->add_method('initialize' => sub { # 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 @@ -181,7 +268,7 @@ Class::MOP::Module->meta->add_attribute( # 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 @@ -197,7 +284,7 @@ Class::MOP::Module->meta->add_attribute( ## 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 @@ -207,14 +294,12 @@ Class::MOP::Class->meta->add_attribute( # 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 @@ -226,7 +311,7 @@ Class::MOP::Class->meta->add_attribute( ); Class::MOP::Class->meta->add_attribute( - Class::MOP::Attribute->new('@!superclasses' => ( + Class::MOP::Attribute->new('superclasses' => ( accessor => { # NOTE: # we just alias the original method @@ -239,33 +324,31 @@ Class::MOP::Class->meta->add_attribute( ); 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 @@ -275,7 +358,6 @@ Class::MOP::Class->meta->add_attribute( # rather than re-produce it here 'instance_metaclass' => \&Class::MOP::Class::instance_metaclass }, - init_arg => 'instance_metaclass', default => 'Class::MOP::Instance', )) ); @@ -290,8 +372,7 @@ Class::MOP::Class->meta->add_attribute( ## 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 @@ -305,8 +386,7 @@ Class::MOP::Attribute->meta->add_attribute( ); 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 @@ -320,115 +400,75 @@ Class::MOP::Attribute->meta->add_attribute( ); 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, @_); @@ -436,14 +476,35 @@ Class::MOP::Attribute->meta->add_method('clone' => sub { ## -------------------------------------------------------- ## 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_method('clone' => sub { + my $self = shift; + $self->meta->clone_object($self, @_); +}); + ## -------------------------------------------------------- ## Class::MOP::Method::Wrapped @@ -453,16 +514,16 @@ Class::MOP::Method->meta->add_attribute( # 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, )) ); @@ -470,8 +531,7 @@ Class::MOP::Method::Generated->meta->add_attribute( ## 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 }, @@ -479,28 +539,26 @@ Class::MOP::Method::Accessor->meta->add_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 }, @@ -515,13 +573,46 @@ Class::MOP::Method::Constructor->meta->add_attribute( # 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('@!slots') + 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('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 @@ -533,8 +624,10 @@ Class::MOP::Instance->meta->add_attribute( # 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 @@ -748,6 +841,11 @@ We set this constant depending on what version perl we are on, this allows us to take advantage of new 5.10 features and stay backwards compat. +=item I + +Whether or not C provides C, a much faster way to get all the +subclasses of a certain class. + =back =head2 Utility functions @@ -785,6 +883,21 @@ 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 + +B + +If possible, we will load the L module and this will function +as C does, otherwise it will just return the C<$code> +argument. + +=item B + +If L is available, this returns true under global +destruction. + +Otherwise it's a constant returning false. + =back =head2 Metaclass cache functions