From: gfx Date: Sun, 16 Aug 2009 01:51:35 +0000 (+0900) Subject: Merge branch 'master' into topic/symbol-manipulator X-Git-Tag: 0.92_01~25 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=c8fd7a1e8d0bd8db0b3d7ea745c491e2ce24decd;hp=2b1fb7dc06b3d3079376692fba02eeba0803504c;p=gitmo%2FClass-MOP.git Merge branch 'master' into topic/symbol-manipulator Conflicts: lib/Class/MOP/Class.pm xs/Package.xs --- diff --git a/Changes b/Changes index 8d6bcff..f7d39fb 100644 --- a/Changes +++ b/Changes @@ -1,25 +1,52 @@ Revision history for Perl extension Class-MOP. -0.90 - Japan Perl Association has sponsored Goro Fuji to improve - startup performance of Class::MOP and Moose. These enhancements - may break backwards compatibility if you're doing (or using) - complex metaprogramming, so, as always, test your code! +0.92 Thu Aug 13, 2009 + * Class::MOP::Class + * Class::MOP::Package + - Move get_method_map and its various scaffolding into Package. (hdp) + + * Class::MOP::Method + - Allow Class::MOP::Method->wrap to take a Class::MOP::Method object as + the first argument, rather than just a coderef. (doy) + + * Class::MOP::Attribute + * Class::MOP::Class + - Allow attribute names to be false (while still requiring them to be + defined). (rafl) + +0.91 Wed Jul 29, 2009 + * Class::MOP::Method::Wrapped + - Fixing variable usage issues with the patch from previous + version, not properly using lexicals in the for + loops. (stevan) + +0.90 Tue Jul 21, 2009 + Japan Perl Association has sponsored Goro Fuji to improve startup + performance of Class::MOP and Moose. These enhancements may break + backwards compatibility if you're doing (or using) complex + metaprogramming, so, as always, test your code! http://blog.perlassociation.org/2009/07/jpa-sponsors-moose-class-mop-work.html * Class::MOP::Class * XS - - Anonymous classes were not destroyed properly when they went - out of scope, leading to a memory leak. RT #47480 (Goro Fuji). + - Anonymous classes were not completely destroyed when they went + out of scope, leading to a memory leak. RT #47480. (Goro + Fuji). * Class::MOP::Class - - get_method, has_method, and add_method no longer use get_method_map. - Methods are more lazily instantiated to improve performance pretty - significantly (Goro Fuji) + - The get_method, has_method, and add_method methods no longer + use get_method_map. Method objects are instantiated + lazily. This significantly improves Class::MOP's load + time. (Goro Fuji) * All classes - - Inline fewer metaclass-level constructors since the ones we have are - perfectly fine. This reduces the number of string evals (Goro Fuji) + - Inline fewer metaclass-level constructors since the ones we + have are perfectly fine. This reduces the number of string + evals. (Goro Fuji) + + * Class::MOP::Method::Wrapped + - If a method modifier set $_, this caused the modifier to blow + up, because of some weird internals. (Jeremy Stashewsky) 0.89 Fri Jul 3, 2009 * Class::MOP::Class @@ -618,7 +645,7 @@ Revision history for Perl extension Class-MOP. the symbol table as methods (these are optimized constant subs) 0.61 Fri. June 13, 2008 - - Okay, lets give this another try and see if PAUSE + - Okay, lets give this another try and see if PAUSE recognizes it correct this time. 0.60 Thurs. Jun 12, 2008 @@ -629,21 +656,21 @@ Revision history for Perl extension Class-MOP. !! Several fixes resulting in yet another 25-30% speedup !! * Class::MOP::Class - - now stores the instance of the instance + - now stores the instance of the instance metaclass to avoid needless recomputation and deletes it when the cache is blown - - introduce methods to query Class::MOP::Class for + - introduce methods to query Class::MOP::Class for the options used to make it immutable as well as - the proper immutable transformer. (groditi) + the proper immutable transformer. (groditi) * Class::MOP::Package - - {add, has, get, remove}_package_symbol all + - {add, has, get, remove}_package_symbol all now accept a HASH ref argument as well as the string. All internal usages now use the HASH ref version. * Class::MOP - - MOP.xs does sanity checks on the coderef + - MOP.xs does sanity checks on the coderef to avoid a segfault - is_class_loaded check now uses code that was improved in Moose's ClassName type @@ -653,19 +680,19 @@ Revision history for Perl extension Class-MOP. load_class (Sartak) - tests for this and other aspects of load_class (Sartak) - + * Class::MOP - Class::MOP::Class + Class::MOP::Class Class::MOP::Method Class::MOP::Method::Wrapped Class::MOP::Attribute - - switched usage of reftype to ref because + - switched usage of reftype to ref because it is much faster 0.58 Thurs. May 29, 2008 (late night release engineering)-- - - - fixing the version is META.yml, no functional + + - fixing the version is META.yml, no functional changes in this release 0.57 Wed. May 28, 2008 @@ -677,13 +704,13 @@ Revision history for Perl extension Class-MOP. instead of manually grabbing each symbol - streamlining &initialize somewhat, since it gets called so much - + * Class::MOP::Package - - made {get, has}_package_symbol not call - &namespace so much - - inlining a few calls to &name with + - made {get, has}_package_symbol not call + &namespace so much + - inlining a few calls to &name with direct HASH access key access - - added get_all_package_symbols to fetch + - added get_all_package_symbols to fetch a HASH of items based on a type filter similar to list_all_package_symbols - added tests for this @@ -692,7 +719,7 @@ Revision history for Perl extension Class-MOP. Class::MOP::Method::Constructor Class::MOP::Method::Generated Class::MOP::Method::Accessor - - added more descriptive error message to help + - added more descriptive error message to help keep people from wasting time tracking an error that is easily fixed by upgrading. @@ -705,7 +732,7 @@ Revision history for Perl extension Class-MOP. * Class::MOP - we now get the &check_package_cache_flag function from MRO::Compat - - All XS based functionality now has a + - All XS based functionality now has a Pure Perl alternative - the CLASS_MOP_NO_XS environment variable can now be used to force non-XS versions @@ -740,15 +767,15 @@ Revision history for Perl extension Class-MOP. Class::MOP::Method::Generated Class::MOP::Method::Accessor Class::MOP::Method::Consructor - - the &wrap constructor method now requires that a - 'package_name' and 'name' attribute are passed. This - is to help support the no-XS version, and will - throw an error if these are not supplied. + - the &wrap constructor method now requires that a + 'package_name' and 'name' attribute are passed. This + is to help support the no-XS version, and will + throw an error if these are not supplied. - all these classes are now bootstrapped properly and now store the package_name and name attributes - correctly as well + correctly as well - ~ Build.PL has been removed since the + ~ Build.PL has been removed since the Module::Install support has been removed 0.55 Mon. April 28, 2008 diff --git a/Makefile.PL b/Makefile.PL index 8610c40..a872582 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -22,7 +22,7 @@ requires 'Sub::Name' => '0.04'; requires 'Task::Weaken'; test_requires 'File::Spec'; -test_requires 'Test::More' => '0.77'; +test_requires 'Test::More' => '0.88'; test_requires 'Test::Exception' => '0.27'; extra_tests(); diff --git a/README b/README index 67286fd..3ff3e79 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Class::MOP version 0.89 +Class::MOP version 0.92 =========================== See the individual module documentation for more information @@ -15,11 +15,12 @@ To install this module type the following: DEPENDENCIES This module requires these other modules and libraries: - + + Devel::GlobalDestruction + MRO::Compat Scalar::Util Sub::Name - Carp - B + Task::Weaken COPYRIGHT AND LICENCE diff --git a/bench/loading-benchmark.pl b/bench/loading-benchmark.pl index 2994f6c..dc0184c 100755 --- a/bench/loading-benchmark.pl +++ b/bench/loading-benchmark.pl @@ -6,13 +6,21 @@ my($count, $module) = @ARGV; $count ||= 10; $module ||= 'Moose'; +my @blib = qw(-Iblib/lib -Iblib/arch -I../Moose/blib/lib -I../Moose/blib/arch -I../Moose/lib); + +$| = 1; # autoflush + +print 'Installed: '; +system $^X, '-le', 'require Moose; print $INC{q{Moose.pm}}'; + +print 'Blead: '; +system $^X, @blib, '-le', 'require Moose; print $INC{q{Moose.pm}}'; + cmpthese timethese $count => { released => sub { - system( $^X, '-e', "require $module" ) == 0 or die; + system( $^X, '-e', "require $module") == 0 or die; }, blead => sub { - system( $^X, '-Iblib/lib', '-Iblib/arch', '-e', "require $module" ) - == 0 - or die; + system( $^X, @blib, '-e', "require $module") == 0 or die; }, }; diff --git a/lib/Class/MOP.pm b/lib/Class/MOP.pm index cf80489..8f2f6ac 100644 --- a/lib/Class/MOP.pm +++ b/lib/Class/MOP.pm @@ -29,7 +29,7 @@ BEGIN { *check_package_cache_flag = \&mro::get_pkg_gen; } -our $VERSION = '0.89'; +our $VERSION = '0.92'; our $XS_VERSION = $VERSION; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; @@ -219,6 +219,42 @@ Class::MOP::Package->meta->add_attribute( )) ); +Class::MOP::Package->meta->add_attribute( + Class::MOP::Attribute->new('methods' => ( + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'get_method_map' => \&Class::MOP::Package::get_method_map + }, + default => sub { {} } + )) +); + +Class::MOP::Package->meta->add_attribute( + Class::MOP::Attribute->new('method_metaclass' => ( + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'method_metaclass' => \&Class::MOP::Package::method_metaclass + }, + default => 'Class::MOP::Method', + )) +); + +Class::MOP::Package->meta->add_attribute( + Class::MOP::Attribute->new('wrapped_method_metaclass' => ( + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'wrapped_method_metaclass' => \&Class::MOP::Package::wrapped_method_metaclass + }, + default => 'Class::MOP::Method::Wrapped', + )) +); + ## -------------------------------------------------------- ## Class::MOP::Module @@ -283,18 +319,6 @@ Class::MOP::Class->meta->add_attribute( ); Class::MOP::Class->meta->add_attribute( - Class::MOP::Attribute->new('methods' => ( - reader => { - # NOTE: - # we just alias the original method - # rather than re-produce it here - 'get_method_map' => \&Class::MOP::Class::get_method_map - }, - default => sub { {} } - )) -); - -Class::MOP::Class->meta->add_attribute( Class::MOP::Attribute->new('superclasses' => ( accessor => { # NOTE: @@ -320,30 +344,6 @@ Class::MOP::Class->meta->add_attribute( ); Class::MOP::Class->meta->add_attribute( - 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 - }, - default => 'Class::MOP::Method', - )) -); - -Class::MOP::Class->meta->add_attribute( - Class::MOP::Attribute->new('wrapped_method_metaclass' => ( - reader => { - # NOTE: - # we just alias the original method - # rather than re-produce it here - 'wrapped_method_metaclass' => \&Class::MOP::Class::wrapped_method_metaclass - }, - default => 'Class::MOP::Method::Wrapped', - )) -); - -Class::MOP::Class->meta->add_attribute( Class::MOP::Attribute->new('instance_metaclass' => ( reader => { # NOTE: we need to do this in order diff --git a/lib/Class/MOP/Attribute.pm b/lib/Class/MOP/Attribute.pm index e605533..8f30cc2 100644 --- a/lib/Class/MOP/Attribute.pm +++ b/lib/Class/MOP/Attribute.pm @@ -9,7 +9,7 @@ use Class::MOP::Method::Accessor; use Carp 'confess'; use Scalar::Util 'blessed', 'weaken'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; @@ -32,7 +32,7 @@ sub new { my $name = $options{name}; - (defined $name && $name) + (defined $name) || confess "You must provide a name for the attribute"; $options{init_arg} = $name diff --git a/lib/Class/MOP/Class.pm b/lib/Class/MOP/Class.pm index fbceff6..d35f33e 100644 --- a/lib/Class/MOP/Class.pm +++ b/lib/Class/MOP/Class.pm @@ -11,10 +11,10 @@ use Class::MOP::Method::Constructor; use Carp 'confess'; use Scalar::Util 'blessed', 'reftype', 'weaken'; -use Sub::Name 'subname'; +use Sub::Name 'subname'; use Devel::GlobalDestruction 'in_global_destruction'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; @@ -349,16 +349,12 @@ sub create { sub get_attribute_map { $_[0]->{'attributes'} } sub attribute_metaclass { $_[0]->{'attribute_metaclass'} } -sub method_metaclass { $_[0]->{'method_metaclass'} } -sub wrapped_method_metaclass { $_[0]->{'wrapped_method_metaclass'} } sub instance_metaclass { $_[0]->{'instance_metaclass'} } sub immutable_trait { $_[0]->{'immutable_trait'} } sub constructor_class { $_[0]->{'constructor_class'} } sub constructor_name { $_[0]->{'constructor_name'} } sub destructor_class { $_[0]->{'destructor_class'} } -sub _method_map { $_[0]->{'methods'} } - # Instance Construction & Cloning sub new_object { @@ -601,48 +597,6 @@ sub class_precedence_list { ## Methods -sub wrap_method_body { - my ( $self, %args ) = @_; - - ('CODE' eq ref $args{body}) - || confess "Your code block must be a CODE reference"; - - $self->method_metaclass->wrap( - package_name => $self->name, - %args, - ); -} - -sub add_method { - my ($self, $method_name, $method) = @_; - (defined $method_name && $method_name) - || confess "You must define a method name"; - - my $body; - if (blessed($method)) { - $body = $method->body; - if ($method->package_name ne $self->name) { - $method = $method->clone( - package_name => $self->name, - name => $method_name - ) if $method->can('clone'); - } - - $method->attach_to_class($self); - $self->_method_map->{$method_name} = $method; - } - else { - # If a raw code reference is supplied, its method object is not created. - # The method object won't be created until required. - $body = $method; - } - - $self->add_package_symbol( - { sigil => '&', type => 'CODE', name => $method_name }, - $body, - ); -} - { my $fetch_and_prepare_method = sub { my ($self, $method_name) = @_; @@ -725,77 +679,6 @@ sub alias_method { shift->add_method(@_); } -sub _code_is_mine { - my ( $self, $code ) = @_; - - my ( $code_package, $code_name ) = Class::MOP::get_code_info($code); - - return $code_package && $code_package eq $self->name - || ( $code_package eq 'constant' && $code_name eq '__ANON__' ); -} - -sub has_method { - my ($self, $method_name) = @_; - (defined $method_name && $method_name) - || confess "You must define a method name"; - - return defined($self->get_method($method_name)); -} - -sub get_method { - my ($self, $method_name) = @_; - (defined $method_name && $method_name) - || confess "You must define a method name"; - - my $method_map = $self->_method_map; - my $method_object = $method_map->{$method_name}; - my $code = $self->get_package_symbol({ - name => $method_name, - sigil => '&', - type => 'CODE', - }); - - unless ( $method_object && $method_object->body == ( $code || 0 ) ) { - if ( $code && $self->_code_is_mine($code) ) { - $method_object = $method_map->{$method_name} - = $self->wrap_method_body( - body => $code, - name => $method_name, - associated_metaclass => $self, - ); - } - else { - delete $method_map->{$method_name}; - return undef; - } - } - - return $method_object; -} - -sub remove_method { - my ($self, $method_name) = @_; - (defined $method_name && $method_name) - || confess "You must define a method name"; - - my $removed_method = delete $self->get_method_map->{$method_name}; - - $self->remove_package_symbol( - { sigil => '&', type => 'CODE', name => $method_name } - ); - - $removed_method->detach_from_class if $removed_method; - - $self->update_package_cache_flag; # still valid, since we just removed the method from the map - - return $removed_method; -} - -sub get_method_list { - my $self = shift; - return grep { $self->has_method($_) } keys %{ $self->namespace }; -} - sub find_method_by_name { my ($self, $method_name) = @_; (defined $method_name && $method_name) @@ -976,14 +859,14 @@ sub invalidate_meta_instance { sub has_attribute { my ($self, $attribute_name) = @_; - (defined $attribute_name && $attribute_name) + (defined $attribute_name) || confess "You must define an attribute name"; exists $self->get_attribute_map->{$attribute_name}; } sub get_attribute { my ($self, $attribute_name) = @_; - (defined $attribute_name && $attribute_name) + (defined $attribute_name) || confess "You must define an attribute name"; return $self->get_attribute_map->{$attribute_name} # NOTE: @@ -994,7 +877,7 @@ sub get_attribute { sub remove_attribute { my ($self, $attribute_name) = @_; - (defined $attribute_name && $attribute_name) + (defined $attribute_name) || confess "You must define an attribute name"; my $removed_attribute = $self->get_attribute_map->{$attribute_name}; return unless defined $removed_attribute; @@ -1549,50 +1432,14 @@ include indirect subclasses. =back -=head2 Method introspection and creation - -These methods allow you to introspect a class's methods, as well as -add, remove, or change methods. - -Determining what is truly a method in a Perl 5 class requires some -heuristics (aka guessing). +=head2 Method introspection -Methods defined outside the package with a fully qualified name (C) will be included. Similarly, methods named -with a fully qualified name using L are also included. - -However, we attempt to ignore imported functions. - -Ultimately, we are using heuristics to determine what truly is a -method in a class, and these heuristics may get the wrong answer in -some edge cases. However, for most "normal" cases the heuristics work -correctly. +See L for +methods that operate only on the current class. Class::MOP::Class adds +introspection capabilities that take inheritance into account. =over 4 -=item B<< $metaclass->get_method($method_name) >> - -This will return a L for the specified -C<$method_name>. If the class does not have the specified method, it -returns C - -=item B<< $metaclass->has_method($method_name) >> - -Returns a boolean indicating whether or not the class defines the -named method. It does not include methods inherited from parent -classes. - -=item B<< $metaclass->get_method_map >> - -Returns a hash reference representing the methods defined in this -class. The keys are method names and the values are -L objects. - -=item B<< $metaclass->get_method_list >> - -This will return a list of method I for all methods defined in -this class. - =item B<< $metaclass->get_all_methods >> This will traverse the inheritance hierarchy and return a list of all @@ -1630,38 +1477,6 @@ This method returns the first method in any superclass matching the given name. It is effectively the method that C would dispatch to. -=item B<< $metaclass->add_method($method_name, $method) >> - -This method takes a method name and a subroutine reference, and adds -the method to the class. - -The subroutine reference can be a L, and you are -strongly encouraged to pass a meta method object instead of a code -reference. If you do so, that object gets stored as part of the -class's method map directly. If not, the meta information will have to -be recreated later, and may be incorrect. - -If you provide a method object, this method will clone that object if -the object's package name does not match the class name. This lets us -track the original source of any methods added from other classes -(notably Moose roles). - -=item B<< $metaclass->remove_method($method_name) >> - -Remove the named method from the class. This method returns the -L object for the method. - -=item B<< $metaclass->method_metaclass >> - -Returns the class name of the method metaclass, see -L for more information on the method metaclass. - -=item B<< $metaclass->wrapped_method_metaclass >> - -Returns the class name of the wrapped method metaclass, see -L for more information on the wrapped -method metaclass. - =back =head2 Attribute introspection and creation diff --git a/lib/Class/MOP/Class/Immutable/Trait.pm b/lib/Class/MOP/Class/Immutable/Trait.pm index bb1b7e1..29c79e8 100644 --- a/lib/Class/MOP/Class/Immutable/Trait.pm +++ b/lib/Class/MOP/Class/Immutable/Trait.pm @@ -8,7 +8,7 @@ use MRO::Compat; use Carp 'confess'; use Scalar::Util 'blessed', 'weaken'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; diff --git a/lib/Class/MOP/Instance.pm b/lib/Class/MOP/Instance.pm index b617afa..fc418d8 100644 --- a/lib/Class/MOP/Instance.pm +++ b/lib/Class/MOP/Instance.pm @@ -6,7 +6,7 @@ use warnings; use Scalar::Util 'weaken', 'blessed'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; @@ -181,7 +181,7 @@ sub inline_create_instance { sub inline_slot_access { my ($self, $instance, $slot_name) = @_; - sprintf q[%s->{'%s'}], $instance, quotemeta($slot_name); + sprintf q[%s->{"%s"}], $instance, quotemeta($slot_name); } sub inline_get_slot_value { diff --git a/lib/Class/MOP/Method.pm b/lib/Class/MOP/Method.pm index 3b6d025..9a0cdda 100644 --- a/lib/Class/MOP/Method.pm +++ b/lib/Class/MOP/Method.pm @@ -5,9 +5,9 @@ use strict; use warnings; use Carp 'confess'; -use Scalar::Util 'weaken', 'reftype'; +use Scalar::Util 'weaken', 'reftype', 'blessed'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; @@ -28,8 +28,15 @@ sub wrap { my %params = @args; my $code = $params{body}; - (ref $code && 'CODE' eq reftype($code)) - || confess "You must supply a CODE reference to bless, not (" . ($code || 'undef') . ")"; + if (blessed($code) && $code->isa(__PACKAGE__)) { + my $method = $code->clone; + delete $params{body}; + Class::MOP::class_of($class)->rebless_instance($method, %params); + return $method; + } + elsif (!ref $code || 'CODE' ne reftype($code)) { + confess "You must supply a CODE reference to bless, not (" . ($code || 'undef') . ")"; + } ($params{package_name} && $params{name}) || confess "You must supply the package_name and name parameters"; @@ -144,8 +151,9 @@ introspection interface. =item B<< Class::MOP::Method->wrap($code, %options) >> -This is the constructor. It accepts a subroutine reference and a hash -of options. +This is the constructor. It accepts a method body in the form of +either a code reference or a L instance, followed +by a hash of options. The options are: @@ -153,11 +161,13 @@ The options are: =item * name -The method name (without a package name). This is required. +The method name (without a package name). This is required if C<$code> +is a coderef. =item * package_name -The package name for the method. This is required. +The package name for the method. This is required if C<$code> is a +coderef. =item * associated_metaclass diff --git a/lib/Class/MOP/Method/Accessor.pm b/lib/Class/MOP/Method/Accessor.pm index 59eb20c..a92b5bc 100644 --- a/lib/Class/MOP/Method/Accessor.pm +++ b/lib/Class/MOP/Method/Accessor.pm @@ -7,7 +7,7 @@ use warnings; use Carp 'confess'; use Scalar::Util 'blessed', 'weaken'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; diff --git a/lib/Class/MOP/Method/Constructor.pm b/lib/Class/MOP/Method/Constructor.pm index c1ac2b3..e145a48 100644 --- a/lib/Class/MOP/Method/Constructor.pm +++ b/lib/Class/MOP/Method/Constructor.pm @@ -7,7 +7,7 @@ use warnings; use Carp 'confess'; use Scalar::Util 'blessed', 'weaken', 'looks_like_number'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; diff --git a/lib/Class/MOP/Method/Generated.pm b/lib/Class/MOP/Method/Generated.pm index a1e9632..1481e96 100644 --- a/lib/Class/MOP/Method/Generated.pm +++ b/lib/Class/MOP/Method/Generated.pm @@ -6,12 +6,14 @@ use warnings; use Carp 'confess'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; use base 'Class::MOP::Method'; +use constant _PRINT_SOURCE => $ENV{MOP_PRINT_SOURCE} ? 1 : 0; + ## accessors sub new { @@ -35,7 +37,7 @@ sub _eval_closure { my $e = do { local $@; local $SIG{__DIE__}; - $code = eval join + my $source = join "\n", ( map { /^([\@\%\$])/ @@ -48,6 +50,8 @@ sub _eval_closure { } keys %$__captures ), $_[2]; + print STDERR $_[0]->name, ' ', $source, "\n" if _PRINT_SOURCE; + $code = eval $source; $@; }; diff --git a/lib/Class/MOP/Method/Inlined.pm b/lib/Class/MOP/Method/Inlined.pm index 26b4887..fa68ebe 100644 --- a/lib/Class/MOP/Method/Inlined.pm +++ b/lib/Class/MOP/Method/Inlined.pm @@ -6,7 +6,7 @@ use warnings; use Carp 'confess'; use Scalar::Util 'blessed', 'weaken', 'looks_like_number', 'refaddr'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; diff --git a/lib/Class/MOP/Method/Wrapped.pm b/lib/Class/MOP/Method/Wrapped.pm index 4e72a59..0f75ced 100644 --- a/lib/Class/MOP/Method/Wrapped.pm +++ b/lib/Class/MOP/Method/Wrapped.pm @@ -7,7 +7,7 @@ use warnings; use Carp 'confess'; use Scalar::Util 'blessed'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; @@ -28,7 +28,7 @@ my $_build_wrapped_method = sub { ); if (@$before && @$after) { $modifier_table->{cache} = sub { - $_->(@_) for @{$before}; + for my $c (@$before) { $c->(@_) }; my @rval; ((defined wantarray) ? ((wantarray) ? @@ -37,14 +37,14 @@ my $_build_wrapped_method = sub { ($rval[0] = $around->{cache}->(@_))) : $around->{cache}->(@_)); - $_->(@_) for @{$after}; + for my $c (@$after) { $c->(@_) }; return unless defined wantarray; return wantarray ? @rval : $rval[0]; } } elsif (@$before && !@$after) { $modifier_table->{cache} = sub { - $_->(@_) for @{$before}; + for my $c (@$before) { $c->(@_) }; return $around->{cache}->(@_); } } @@ -58,7 +58,7 @@ my $_build_wrapped_method = sub { ($rval[0] = $around->{cache}->(@_))) : $around->{cache}->(@_)); - $_->(@_) for @{$after}; + for my $c (@$after) { $c->(@_) }; return unless defined wantarray; return wantarray ? @rval : $rval[0]; } @@ -70,10 +70,10 @@ my $_build_wrapped_method = sub { sub wrap { my ( $class, $code, %params ) = @_; - + (blessed($code) && $code->isa('Class::MOP::Method')) || confess "Can only wrap blessed CODE"; - + my $modifier_table = { cache => undef, orig => $code, @@ -87,7 +87,7 @@ sub wrap { $_build_wrapped_method->($modifier_table); return $class->SUPER::wrap( sub { $modifier_table->{cache}->(@_) }, - # get these from the original + # get these from the original # unless explicitly overriden package_name => $params{package_name} || $code->package_name, name => $params{name} || $code->name, diff --git a/lib/Class/MOP/Module.pm b/lib/Class/MOP/Module.pm index d233ec3..e7ce4d1 100644 --- a/lib/Class/MOP/Module.pm +++ b/lib/Class/MOP/Module.pm @@ -7,7 +7,7 @@ use warnings; use Carp 'confess'; use Scalar::Util 'blessed'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; diff --git a/lib/Class/MOP/Object.pm b/lib/Class/MOP/Object.pm index 93649bc..c11ca0e 100644 --- a/lib/Class/MOP/Object.pm +++ b/lib/Class/MOP/Object.pm @@ -6,7 +6,7 @@ use warnings; use Scalar::Util 'blessed'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; diff --git a/lib/Class/MOP/Package.pm b/lib/Class/MOP/Package.pm index 38269d2..f2c8e44 100644 --- a/lib/Class/MOP/Package.pm +++ b/lib/Class/MOP/Package.pm @@ -6,8 +6,9 @@ use warnings; use Scalar::Util 'blessed', 'reftype'; use Carp 'confess'; +use Sub::Name 'subname'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; @@ -48,8 +49,12 @@ sub reinitialize { my %options = @args; my $package_name = delete $options{package}; - (defined $package_name && $package_name && !blessed($package_name)) - || confess "You must pass a package name and it cannot be blessed"; + (defined $package_name && $package_name + && (!blessed $package_name || $package_name->isa('Class::MOP::Package'))) + || confess "You must pass a package name or an existing Class::MOP::Package instance"; + + $package_name = $package_name->name + if blessed $package_name; Class::MOP::remove_metaclass_by_name($package_name); @@ -99,6 +104,11 @@ sub namespace { \%{$_[0]->{'package'} . '::'} } +sub method_metaclass { $_[0]->{'method_metaclass'} } +sub wrapped_method_metaclass { $_[0]->{'wrapped_method_metaclass'} } + +sub _method_map { $_[0]->{'methods'} } + # utility methods { @@ -208,6 +218,129 @@ sub list_all_package_symbols { } } +## Methods + +sub wrap_method_body { + my ( $self, %args ) = @_; + + ('CODE' eq ref $args{body}) + || confess "Your code block must be a CODE reference"; + + $self->method_metaclass->wrap( + package_name => $self->name, + %args, + ); +} + +sub add_method { + my ($self, $method_name, $method) = @_; + (defined $method_name && $method_name) + || confess "You must define a method name"; + + my $body; + if (blessed($method)) { + $body = $method->body; + if ($method->package_name ne $self->name) { + $method = $method->clone( + package_name => $self->name, + name => $method_name + ) if $method->can('clone'); + } + + $method->attach_to_class($self); + $self->_method_map->{$method_name} = $method; + } + else { + # If a raw code reference is supplied, its method object is not created. + # The method object won't be created until required. + $body = $method; + } + + + my ( $current_package, $current_name ) = Class::MOP::get_code_info($body); + + if ( !defined $current_name || $current_name eq '__ANON__' ) { + my $full_method_name = ($self->name . '::' . $method_name); + subname($full_method_name => $body); + } + + $self->add_package_symbol( + { sigil => '&', type => 'CODE', name => $method_name }, + $body, + ); +} + +sub _code_is_mine { + my ( $self, $code ) = @_; + + my ( $code_package, $code_name ) = Class::MOP::get_code_info($code); + + return $code_package && $code_package eq $self->name + || ( $code_package eq 'constant' && $code_name eq '__ANON__' ); +} + +sub has_method { + my ($self, $method_name) = @_; + (defined $method_name && $method_name) + || confess "You must define a method name"; + + return defined($self->get_method($method_name)); +} + +sub get_method { + my ($self, $method_name) = @_; + (defined $method_name && $method_name) + || confess "You must define a method name"; + + my $method_map = $self->_method_map; + my $method_object = $method_map->{$method_name}; + my $code = $self->get_package_symbol({ + name => $method_name, + sigil => '&', + type => 'CODE', + }); + + unless ( $method_object && $method_object->body == ( $code || 0 ) ) { + if ( $code && $self->_code_is_mine($code) ) { + $method_object = $method_map->{$method_name} + = $self->wrap_method_body( + body => $code, + name => $method_name, + associated_metaclass => $self, + ); + } + else { + delete $method_map->{$method_name}; + return undef; + } + } + + return $method_object; +} + +sub remove_method { + my ($self, $method_name) = @_; + (defined $method_name && $method_name) + || confess "You must define a method name"; + + my $removed_method = delete $self->get_method_map->{$method_name}; + + $self->remove_package_symbol( + { sigil => '&', type => 'CODE', name => $method_name } + ); + + $removed_method->detach_from_class if $removed_method; + + $self->update_package_cache_flag; # still valid, since we just removed the method from the map + + return $removed_method; +} + +sub get_method_list { + my $self = shift; + return grep { $self->has_method($_) } keys %{ $self->namespace }; +} + 1; __END__ @@ -234,10 +367,12 @@ This method creates a new C instance which represents specified package. If an existing metaclass object exists for the package, that will be returned instead. -=item B<< Class::MOP::Package->reinitialize($package_name) >> +=item B<< Class::MOP::Package->reinitialize($package) >> This method forcibly removes any existing metaclass for the package -before calling C +before calling C. In contrast to C, you may +also pass an existing C instance instead of just +a package name as C<$package>. Do not call this unless you know what you are doing. @@ -295,6 +430,84 @@ This works much like C, but it returns a hash reference. The keys are glob names and the values are references to the value for that name. +=back + +=head2 Method introspection and creation + +These methods allow you to introspect a class's methods, as well as +add, remove, or change methods. + +Determining what is truly a method in a Perl 5 class requires some +heuristics (aka guessing). + +Methods defined outside the package with a fully qualified name (C) will be included. Similarly, methods named +with a fully qualified name using L are also included. + +However, we attempt to ignore imported functions. + +Ultimately, we are using heuristics to determine what truly is a +method in a class, and these heuristics may get the wrong answer in +some edge cases. However, for most "normal" cases the heuristics work +correctly. + +=over 4 + +=item B<< $metapackage->get_method($method_name) >> + +This will return a L for the specified +C<$method_name>. If the class does not have the specified method, it +returns C + +=item B<< $metapackage->has_method($method_name) >> + +Returns a boolean indicating whether or not the class defines the +named method. It does not include methods inherited from parent +classes. + +=item B<< $metapackage->get_method_map >> + +Returns a hash reference representing the methods defined in this +class. The keys are method names and the values are +L objects. + +=item B<< $metapackage->get_method_list >> + +This will return a list of method I for all methods defined in +this class. + +=item B<< $metapackage->add_method($method_name, $method) >> + +This method takes a method name and a subroutine reference, and adds +the method to the class. + +The subroutine reference can be a L, and you are +strongly encouraged to pass a meta method object instead of a code +reference. If you do so, that object gets stored as part of the +class's method map directly. If not, the meta information will have to +be recreated later, and may be incorrect. + +If you provide a method object, this method will clone that object if +the object's package name does not match the class name. This lets us +track the original source of any methods added from other classes +(notably Moose roles). + +=item B<< $metapackage->remove_method($method_name) >> + +Remove the named method from the class. This method returns the +L object for the method. + +=item B<< $metapackage->method_metaclass >> + +Returns the class name of the method metaclass, see +L for more information on the method metaclass. + +=item B<< $metapackage->wrapped_method_metaclass >> + +Returns the class name of the wrapped method metaclass, see +L for more information on the wrapped +method metaclass. + =item B<< Class::MOP::Package->meta >> This will return a L instance for this class. diff --git a/lib/metaclass.pm b/lib/metaclass.pm index 62c6d8b..8ea3c67 100644 --- a/lib/metaclass.pm +++ b/lib/metaclass.pm @@ -7,7 +7,7 @@ use warnings; use Carp 'confess'; use Scalar::Util 'blessed'; -our $VERSION = '0.89'; +our $VERSION = '0.92'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; diff --git a/mop.c b/mop.c index dfb178b..65aa925 100644 --- a/mop.c +++ b/mop.c @@ -93,18 +93,15 @@ mop_get_code_info (SV *coderef, char **pkg, char **name) we hit it without the guard, we segfault. The slightly odd return value strikes me as an improvement (mst) */ -#ifdef isGV_with_GP + if ( isGV_with_GP(CvGV(coderef)) ) { -#endif GV *gv = CvGV(coderef); *pkg = HvNAME( GvSTASH(gv) ? GvSTASH(gv) : CvSTASH(coderef) ); *name = GvNAME( CvGV(coderef) ); -#ifdef isGV_with_GP } else { *pkg = "__UNKNOWN__"; *name = "__ANON__"; } -#endif return 1; } diff --git a/ppport.h b/ppport.h index 3e3d52f..ec2f1cc 100644 --- a/ppport.h +++ b/ppport.h @@ -4,7 +4,7 @@ /* ---------------------------------------------------------------------- - ppport.h -- Perl/Pollution/Portability Version 3.17 + ppport.h -- Perl/Pollution/Portability Version 3.19 Automatically created by Devel::PPPort running under perl 5.010000. @@ -21,7 +21,7 @@ SKIP =head1 NAME -ppport.h - Perl/Pollution/Portability version 3.17 +ppport.h - Perl/Pollution/Portability version 3.19 =head1 SYNOPSIS @@ -232,6 +232,7 @@ same function or variable in your project. my_strlcpy() NEED_my_strlcpy NEED_my_strlcpy_GLOBAL newCONSTSUB() NEED_newCONSTSUB NEED_newCONSTSUB_GLOBAL newRV_noinc() NEED_newRV_noinc NEED_newRV_noinc_GLOBAL + newSV_type() NEED_newSV_type NEED_newSV_type_GLOBAL newSVpvn_flags() NEED_newSVpvn_flags NEED_newSVpvn_flags_GLOBAL newSVpvn_share() NEED_newSVpvn_share NEED_newSVpvn_share_GLOBAL pv_display() NEED_pv_display NEED_pv_display_GLOBAL @@ -377,7 +378,7 @@ use strict; # Disable broken TRIE-optimization BEGIN { eval '${^RE_TRIE_MAXBUF} = -1' if $] >= 5.009004 && $] <= 5.009005 } -my $VERSION = 3.17; +my $VERSION = 3.19; my %opt = ( quiet => 0, @@ -486,6 +487,7 @@ G_NOARGS||| G_SCALAR||| G_VOID||5.004000| GetVars||| +GvSVn|5.009003||p GvSV||| Gv_AMupdate||| HEf_SVKEY||5.004000| @@ -498,6 +500,8 @@ HeSVKEY_set||5.004000| HeSVKEY||5.004000| HeUTF8||5.011000| HeVAL||5.004000| +HvNAMELEN_get|5.009003||p +HvNAME_get|5.009003||p HvNAME||| INT2PTR|5.006000||p IN_LOCALE_COMPILETIME|5.007002||p @@ -628,6 +632,9 @@ PERL_SHORT_MAX|5.004000||p PERL_SHORT_MIN|5.004000||p PERL_SIGNALS_UNSAFE_FLAG|5.008001||p PERL_SUBVERSION|5.006000||p +PERL_SYS_INIT3||5.006000| +PERL_SYS_INIT||| +PERL_SYS_TERM||5.011000| PERL_UCHAR_MAX|5.004000||p PERL_UCHAR_MIN|5.004000||p PERL_UINT_MAX|5.004000||p @@ -661,9 +668,12 @@ PL_diehook|5.004050||p PL_dirty|5.004050||p PL_dowarn|||pn PL_errgv|5.004050||p +PL_error_count|5.011000||p PL_expect|5.011000||p PL_hexdigit|5.005000||p PL_hints|5.005000||p +PL_in_my_stash|5.011000||p +PL_in_my|5.011000||p PL_last_in_gv|||n PL_laststatval|5.005000||p PL_lex_state|5.011000||p @@ -769,6 +779,7 @@ SV_MUTABLE_RETURN|5.009003||p SV_NOSTEAL|5.009002||p SV_SMAGIC|5.009003||p SV_UTF8_NO_ENCODING|5.008001||p +SVfARG|5.009005||p SVf_UTF8|5.006000||p SVf|5.006000||p SVt_IV||| @@ -977,6 +988,7 @@ XPUSHn||| XPUSHp||| XPUSHs||| XPUSHu|5.004000||p +XSPROTO|5.010000||p XSRETURN_EMPTY||| XSRETURN_IV||| XSRETURN_NO||| @@ -1055,7 +1067,6 @@ boolSV|5.004000||p boot_core_PerlIO||| boot_core_UNIVERSAL||| boot_core_mro||| -boot_core_xsutils||| bytes_from_utf8||5.007001| bytes_to_uni|||n bytes_to_utf8||5.006001| @@ -1341,7 +1352,6 @@ get_vtbl||5.005030| getcwd_sv||5.007002| getenv_len||| glob_2number||| -glob_2pv||| glob_assign_glob||| glob_assign_ref||| gp_dup||| @@ -1372,7 +1382,8 @@ gv_fetchmethod_autoload||5.004000| gv_fetchmethod_flags||5.011000| gv_fetchmethod||| gv_fetchmeth||| -gv_fetchpvn_flags||5.009002| +gv_fetchpvn_flags|5.009002||p +gv_fetchpvs|5.009004||p gv_fetchpv||| gv_fetchsv||5.009002| gv_fullname3||5.004000| @@ -1384,7 +1395,7 @@ gv_init_sv||| gv_init||| gv_name_set||5.009004| gv_stashpvn|5.004000||p -gv_stashpvs||5.009003| +gv_stashpvs|5.009003||p gv_stashpv||| gv_stashsv||| he_dup||| @@ -1470,6 +1481,7 @@ isBLANK|5.006001||p isCNTRL|5.006000||p isDIGIT||| isGRAPH|5.006000||p +isGV_with_GP|5.009004||p isLOWER||| isPRINT|5.004000||p isPSXSPC|5.006001||p @@ -1774,7 +1786,7 @@ newSTATEOP||| newSUB||| newSVOP||| newSVREF||| -newSV_type||5.009005| +newSV_type|5.009005||p newSVhek||5.009003| newSViv||| newSVnv||| @@ -2195,6 +2207,7 @@ sv_derived_from||5.004000| sv_destroyable||5.010000| sv_does||5.009004| sv_dump||| +sv_dup_inc_multiple||| sv_dup||| sv_eq||| sv_exp_grow||| @@ -3907,6 +3920,13 @@ typedef NVTYPE NV; return; \ } STMT_END #endif +#ifndef XSPROTO +# define XSPROTO(name) void name(pTHX_ CV* cv) +#endif + +#ifndef SVfARG +# define SVfARG(p) ((void*)(p)) +#endif #ifndef PERL_ABS # define PERL_ABS(x) ((x) < 0 ? -(x) : (x)) #endif @@ -4086,9 +4106,11 @@ extern U32 DPPP_(my_PL_signals); # define PL_dirty dirty # define PL_dowarn dowarn # define PL_errgv errgv +# define PL_error_count error_count # define PL_expect expect # define PL_hexdigit hexdigit # define PL_hints hints +# define PL_in_my in_my # define PL_laststatval laststatval # define PL_lex_state lex_state # define PL_lex_stuff lex_stuff @@ -4171,6 +4193,10 @@ extern yy_parser DPPP_(dummy_PL_parser); # define PL_lex_state D_PPP_my_PL_parser_var(lex_state) # define PL_lex_stuff D_PPP_my_PL_parser_var(lex_stuff) # define PL_tokenbuf D_PPP_my_PL_parser_var(tokenbuf) +# define PL_in_my D_PPP_my_PL_parser_var(in_my) +# define PL_in_my_stash D_PPP_my_PL_parser_var(in_my_stash) +# define PL_error_count D_PPP_my_PL_parser_var(error_count) + #else @@ -4711,6 +4737,35 @@ DPPP_(my_newCONSTSUB)(HV *stash, const char *name, SV *sv) # define SvREFCNT_inc_simple_void_NN(sv) (void)(++SvREFCNT((SV*)(sv))) #endif +#ifndef newSV_type + +#if defined(NEED_newSV_type) +static SV* DPPP_(my_newSV_type)(pTHX_ svtype const t); +static +#else +extern SV* DPPP_(my_newSV_type)(pTHX_ svtype const t); +#endif + +#ifdef newSV_type +# undef newSV_type +#endif +#define newSV_type(a) DPPP_(my_newSV_type)(aTHX_ a) +#define Perl_newSV_type DPPP_(my_newSV_type) + +#if defined(NEED_newSV_type) || defined(NEED_newSV_type_GLOBAL) + +SV* +DPPP_(my_newSV_type)(pTHX_ svtype const t) +{ + SV* const sv = newSV(0); + sv_upgrade(sv, t); + return sv; +} + +#endif + +#endif + #if (PERL_BCDVERSION < 0x5006000) # define D_PPP_CONSTPV_ARG(x) ((char *) (x)) #else @@ -5298,6 +5353,19 @@ DPPP_(my_newSVpvn_share)(pTHX_ const char *src, I32 len, U32 hash) #ifndef SvSHARED_HASH # define SvSHARED_HASH(sv) (0 + SvUVX(sv)) #endif +#ifndef HvNAME_get +# define HvNAME_get(hv) HvNAME(hv) +#endif +#ifndef HvNAMELEN_get +# define HvNAMELEN_get(hv) (HvNAME_get(hv) ? (I32)strlen(HvNAME_get(hv)) : 0) +#endif +#ifndef GvSVn +# define GvSVn(gv) GvSV(gv) +#endif + +#ifndef isGV_with_GP +# define isGV_with_GP(gv) isGV(gv) +#endif #ifndef WARN_ALL # define WARN_ALL 0 #endif @@ -5561,6 +5629,17 @@ DPPP_(my_warner)(U32 err, const char *pat, ...) #ifndef hv_stores # define hv_stores(hv, key, val) hv_store(hv, key "", sizeof(key) - 1, val, 0) #endif +#ifndef gv_fetchpvn_flags +# define gv_fetchpvn_flags(name, len, flags, svt) gv_fetchpv(name, flags, svt) +#endif + +#ifndef gv_fetchpvs +# define gv_fetchpvs(name, flags, svt) gv_fetchpvn_flags(name "", sizeof(name) - 1, flags, svt) +#endif + +#ifndef gv_stashpvs +# define gv_stashpvs(name, flags) gv_stashpvn(name "", sizeof(name) - 1, flags) +#endif #ifndef SvGETMAGIC # define SvGETMAGIC(x) STMT_START { if (SvGMAGICAL(x)) mg_get(x); } STMT_END #endif diff --git a/t/005_attributes.t b/t/005_attributes.t index 1429a0a..b7a545b 100644 --- a/t/005_attributes.t +++ b/t/005_attributes.t @@ -1,7 +1,7 @@ use strict; use warnings; -use Test::More tests => 73; +use Test::More tests => 90; use Test::Exception; use Class::MOP; @@ -225,7 +225,9 @@ is($BAZ_ATTR->name, '$baz', '... got the attributes name correctly'); } '... we added a method to Buzz successfully'; } -{ + + +for(1 .. 2){ my $buzz; ::lives_ok { $buzz = Buzz->meta->new_object } '...Buzz instantiated successfully'; ::is($buzz->foo, 'Buzz', '...foo builder works as expected'); @@ -244,17 +246,15 @@ is($BAZ_ATTR->name, '$baz', '... got the attributes name correctly'); ::ok($buzz2->has_bar, '...bar is set'); ::is($buzz2->bar, undef, '...bar is undef'); -} + my $buzz3; + ::lives_ok { $buzz3 = Buzz->meta->new_object } '...Buzz instantiated successfully'; + ::ok($buzz3->has_bah, '...bah is set'); + ::is($buzz3->bah, 'BAH', '...bah returns "BAH" '); -{ - my $buzz; - ::lives_ok { $buzz = Buzz->meta->new_object } '...Buzz instantiated successfully'; - ::ok($buzz->has_bah, '...bah is set'); - ::is($buzz->bah, 'BAH', '...bah returns "BAH" '); - - my $buzz2; - ::lives_ok { $buzz2 = Buzz->meta->new_object('$bah' => undef) } '...Buzz instantiated successfully'; - ::ok($buzz2->has_bah, '...bah is set'); - ::is($buzz2->bah, undef, '...bah is undef'); + my $buzz4; + ::lives_ok { $buzz4 = Buzz->meta->new_object('$bah' => undef) } '...Buzz instantiated successfully'; + ::ok($buzz4->has_bah, '...bah is set'); + ::is($buzz4->bah, undef, '...bah is undef'); + Buzz->meta->make_immutable(); } diff --git a/t/010_self_introspection.t b/t/010_self_introspection.t index 04504f4..bcc6335 100644 --- a/t/010_self_introspection.t +++ b/t/010_self_introspection.t @@ -34,6 +34,13 @@ my @class_mop_package_methods = qw( add_package_symbol get_package_symbol has_package_symbol remove_package_symbol list_all_package_symbols get_all_package_symbols remove_package_glob + method_metaclass wrapped_method_metaclass + + _method_map + _code_is_mine + has_method get_method add_method remove_method wrap_method_body + get_method_list get_method_map + _deconstruct_variable_name ); @@ -70,15 +77,12 @@ my @class_mop_class_methods = qw( add_dependent_meta_instance remove_dependent_meta_instance invalidate_meta_instances invalidate_meta_instance - attribute_metaclass method_metaclass wrapped_method_metaclass + attribute_metaclass superclasses subclasses direct_subclasses class_precedence_list linearized_isa _superclasses_updated - _method_map - _code_is_mine - has_method get_method add_method remove_method alias_method wrap_method_body - get_method_list get_method_map get_all_method_names get_all_methods compute_all_applicable_methods + alias_method get_all_method_names get_all_methods compute_all_applicable_methods find_method_by_name find_all_methods_by_name find_next_method_by_name add_before_method_modifier add_after_method_modifier add_around_method_modifier @@ -157,6 +161,9 @@ foreach my $non_method_name (qw( my @class_mop_package_attributes = ( 'package', 'namespace', + 'methods', + 'method_metaclass', + 'wrapped_method_metaclass', ); my @class_mop_module_attributes = ( @@ -166,11 +173,8 @@ my @class_mop_module_attributes = ( my @class_mop_class_attributes = ( 'superclasses', - 'methods', 'attributes', 'attribute_metaclass', - 'method_metaclass', - 'wrapped_method_metaclass', 'instance_metaclass', 'immutable_trait', 'constructor_name', @@ -240,6 +244,37 @@ is(ref($class_mop_package_meta->get_attribute('package')->reader), 'HASH', '... ok($class_mop_package_meta->get_attribute('package')->has_init_arg, '... Class::MOP::Class package has a init_arg'); is($class_mop_package_meta->get_attribute('package')->init_arg, 'package', '... Class::MOP::Class package\'s a init_arg is package'); +ok($class_mop_package_meta->get_attribute('method_metaclass')->has_reader, '... Class::MOP::Package method_metaclass has a reader'); +is_deeply($class_mop_package_meta->get_attribute('method_metaclass')->reader, + { 'method_metaclass' => \&Class::MOP::Package::method_metaclass }, + '... Class::MOP::Package method_metaclass\'s a reader is &method_metaclass'); + +ok($class_mop_package_meta->get_attribute('method_metaclass')->has_init_arg, '... Class::MOP::Package method_metaclass has a init_arg'); +is($class_mop_package_meta->get_attribute('method_metaclass')->init_arg, + 'method_metaclass', + '... Class::MOP::Package method_metaclass\'s init_arg is method_metaclass'); + +ok($class_mop_package_meta->get_attribute('method_metaclass')->has_default, '... Class::MOP::Package method_metaclass has a default'); +is($class_mop_package_meta->get_attribute('method_metaclass')->default, + 'Class::MOP::Method', + '... Class::MOP::Package method_metaclass\'s a default is Class::MOP:::Method'); + +ok($class_mop_package_meta->get_attribute('wrapped_method_metaclass')->has_reader, '... Class::MOP::Package wrapped_method_metaclass has a reader'); +is_deeply($class_mop_package_meta->get_attribute('wrapped_method_metaclass')->reader, + { 'wrapped_method_metaclass' => \&Class::MOP::Package::wrapped_method_metaclass }, + '... Class::MOP::Package wrapped_method_metaclass\'s a reader is &wrapped_method_metaclass'); + +ok($class_mop_package_meta->get_attribute('wrapped_method_metaclass')->has_init_arg, '... Class::MOP::Package wrapped_method_metaclass has a init_arg'); +is($class_mop_package_meta->get_attribute('wrapped_method_metaclass')->init_arg, + 'wrapped_method_metaclass', + '... Class::MOP::Package wrapped_method_metaclass\'s init_arg is wrapped_method_metaclass'); + +ok($class_mop_package_meta->get_attribute('method_metaclass')->has_default, '... Class::MOP::Package method_metaclass has a default'); +is($class_mop_package_meta->get_attribute('method_metaclass')->default, + 'Class::MOP::Method', + '... Class::MOP::Package method_metaclass\'s a default is Class::MOP:::Method'); + + # ... class ok($class_mop_class_meta->get_attribute('attributes')->has_reader, '... Class::MOP::Class attributes has a reader'); @@ -272,36 +307,6 @@ is($class_mop_class_meta->get_attribute('attribute_metaclass')->default, 'Class::MOP::Attribute', '... Class::MOP::Class attribute_metaclass\'s a default is Class::MOP:::Attribute'); -ok($class_mop_class_meta->get_attribute('method_metaclass')->has_reader, '... Class::MOP::Class method_metaclass has a reader'); -is_deeply($class_mop_class_meta->get_attribute('method_metaclass')->reader, - { 'method_metaclass' => \&Class::MOP::Class::method_metaclass }, - '... Class::MOP::Class method_metaclass\'s a reader is &method_metaclass'); - -ok($class_mop_class_meta->get_attribute('method_metaclass')->has_init_arg, '... Class::MOP::Class method_metaclass has a init_arg'); -is($class_mop_class_meta->get_attribute('method_metaclass')->init_arg, - 'method_metaclass', - '... Class::MOP::Class method_metaclass\'s init_arg is method_metaclass'); - -ok($class_mop_class_meta->get_attribute('method_metaclass')->has_default, '... Class::MOP::Class method_metaclass has a default'); -is($class_mop_class_meta->get_attribute('method_metaclass')->default, - 'Class::MOP::Method', - '... Class::MOP::Class method_metaclass\'s a default is Class::MOP:::Method'); - -ok($class_mop_class_meta->get_attribute('wrapped_method_metaclass')->has_reader, '... Class::MOP::Class wrapped_method_metaclass has a reader'); -is_deeply($class_mop_class_meta->get_attribute('wrapped_method_metaclass')->reader, - { 'wrapped_method_metaclass' => \&Class::MOP::Class::wrapped_method_metaclass }, - '... Class::MOP::Class wrapped_method_metaclass\'s a reader is &wrapped_method_metaclass'); - -ok($class_mop_class_meta->get_attribute('wrapped_method_metaclass')->has_init_arg, '... Class::MOP::Class wrapped_method_metaclass has a init_arg'); -is($class_mop_class_meta->get_attribute('wrapped_method_metaclass')->init_arg, - 'wrapped_method_metaclass', - '... Class::MOP::Class wrapped_method_metaclass\'s init_arg is wrapped_method_metaclass'); - -ok($class_mop_class_meta->get_attribute('method_metaclass')->has_default, '... Class::MOP::Class method_metaclass has a default'); -is($class_mop_class_meta->get_attribute('method_metaclass')->default, - 'Class::MOP::Method', - '... Class::MOP::Class method_metaclass\'s a default is Class::MOP:::Method'); - # check the values of some of the methods is($class_mop_class_meta->name, 'Class::MOP::Class', '... Class::MOP::Class->name'); diff --git a/t/021_attribute_errors_and_edge_cases.t b/t/021_attribute_errors_and_edge_cases.t index 4de3ff0..b3da244 100644 --- a/t/021_attribute_errors_and_edge_cases.t +++ b/t/021_attribute_errors_and_edge_cases.t @@ -89,11 +89,12 @@ BEGIN {use Class::MOP;use Class::MOP::Attribute; Class::MOP::Attribute->new(); } '... no name argument'; - dies_ok { + # These are no longer errors + lives_ok { Class::MOP::Attribute->new(''); } '... bad name argument'; - dies_ok { + lives_ok { Class::MOP::Attribute->new(0); } '... bad name argument'; } diff --git a/t/030_method.t b/t/030_method.t index e0dbe62..71cc17d 100644 --- a/t/030_method.t +++ b/t/030_method.t @@ -1,7 +1,7 @@ use strict; use warnings; -use Test::More tests => 47; +use Test::More tests => 53; use Test::Exception; use Class::MOP; @@ -137,3 +137,25 @@ is( $clone2->original_name, '__ANON__', '... original_name follows clone chain' ); is( $clone2->original_fully_qualified_name, 'main::__ANON__', '... original_fully_qualified_name follows clone chain' ); + +Class::MOP::Class->create( + 'Method::Subclass', + superclasses => ['Class::MOP::Method'], + attributes => [ + Class::MOP::Attribute->new( + foo => ( + accessor => 'foo', + ) + ), + ], +); + +my $wrapped = Method::Subclass->wrap($method, foo => 'bar'); +isa_ok($wrapped, 'Method::Subclass'); +isa_ok($wrapped, 'Class::MOP::Method'); +is($wrapped->foo, 'bar', 'attribute set properly'); +is($wrapped->package_name, 'main', 'package_name copied properly'); +is($wrapped->name, '__ANON__', 'method name copied properly'); + +my $wrapped2 = Method::Subclass->wrap($method, foo => 'baz', name => 'FOO'); +is($wrapped2->name, 'FOO', 'got a new method name'); diff --git a/t/049_metaclass_reinitialize.t b/t/049_metaclass_reinitialize.t new file mode 100644 index 0000000..a9c0e26 --- /dev/null +++ b/t/049_metaclass_reinitialize.t @@ -0,0 +1,43 @@ +use strict; +use warnings; + +use Test::More; +use Test::Exception; + +{ + package Foo; + use metaclass; + sub foo {} +} + +sub check_meta_sanity { + my ($meta) = @_; + isa_ok($meta, 'Class::MOP::Class'); + is($meta->name, 'Foo'); + ok($meta->has_method('foo')); +} + +can_ok('Foo', 'meta'); + +my $meta = Foo->meta; +check_meta_sanity($meta); + +lives_ok { + $meta = $meta->reinitialize($meta->name); +}; +check_meta_sanity($meta); + +lives_ok { + $meta = $meta->reinitialize($meta); +}; +check_meta_sanity($meta); + +throws_ok { + $meta->reinitialize(''); +} qr/You must pass a package name or an existing Class::MOP::Package instance/; + +throws_ok { + $meta->reinitialize($meta->new_object); +} qr/You must pass a package name or an existing Class::MOP::Package instance/; + +done_testing; diff --git a/t/061_instance_inline.t b/t/061_instance_inline.t index 3856bfe..0141945 100644 --- a/t/061_instance_inline.t +++ b/t/061_instance_inline.t @@ -18,11 +18,11 @@ my $C = 'Class::MOP::Instance'; 'bless {} => $class', '... got the right code for create_instance'); is($C->inline_get_slot_value($instance, $slot_name), - "\$self->{'foo'}", + q[$self->{"foo"}], '... got the right code for get_slot_value'); is($C->inline_set_slot_value($instance, $slot_name, $value), - "\$self->{'foo'} = \$value", + q[$self->{"foo"} = $value], '... got the right code for set_slot_value'); is($C->inline_initialize_slot($instance, $slot_name), @@ -30,18 +30,18 @@ my $C = 'Class::MOP::Instance'; '... got the right code for initialize_slot'); is($C->inline_is_slot_initialized($instance, $slot_name), - "exists \$self->{'foo'}", + q[exists $self->{"foo"}], '... got the right code for get_slot_value'); is($C->inline_weaken_slot_value($instance, $slot_name), - "Scalar::Util::weaken( \$self->{'foo'} )", + q[Scalar::Util::weaken( $self->{"foo"} )], '... got the right code for weaken_slot_value'); is($C->inline_strengthen_slot_value($instance, $slot_name), - "\$self->{'foo'} = \$self->{'foo'}", + q[$self->{"foo"} = $self->{"foo"}], '... got the right code for strengthen_slot_value'); is($C->inline_rebless_instance_structure($instance, $class), - "bless \$self => \$class", + q[bless $self => $class], '... got the right code for rebless_instance_structure'); } diff --git a/t/313_before_after_dollar_under.t b/t/313_before_after_dollar_under.t new file mode 100644 index 0000000..f029173 --- /dev/null +++ b/t/313_before_after_dollar_under.t @@ -0,0 +1,57 @@ +use strict; +use warnings; + +use Class::MOP; +use Class::MOP::Class; +use Test::More qw/no_plan/; +use Test::Exception; + +my %results; + +{ + package Base; + use metaclass; + sub hey { $results{base}++ } +} + +for my $wrap (qw(before after)) { + my $meta = Class::MOP::Class->create_anon_class( + superclasses => ['Base', 'Class::MOP::Object'] + ); + my $alter = "add_${wrap}_method_modifier"; + $meta->$alter('hey' => sub { + $results{wrapped}++; + $_ = 'barf'; # 'barf' would replace the cached wrapper subref + }); + + %results = (); + my $o = $meta->get_meta_instance->create_instance; + isa_ok($o, 'Base'); + lives_ok { + $o->hey; + $o->hey; # this would die with 'Can't use string ("barf") as a subroutine ref while "strict refs" in use' + } 'wrapped doesn\'t die when $_ gets changed'; + is_deeply(\%results, {base=>2,wrapped=>2}); +} + +{ + my $meta = Class::MOP::Class->create_anon_class( + superclasses => ['Base', 'Class::MOP::Object'] + ); + for my $wrap (qw(before after)) { + my $alter = "add_${wrap}_method_modifier"; + $meta->$alter('hey' => sub { + $results{wrapped}++; + $_ = 'barf'; # 'barf' would replace the cached wrapper subref + }); + } + + %results = (); + my $o = $meta->get_meta_instance->create_instance; + isa_ok($o, 'Base'); + lives_ok { + $o->hey; + $o->hey; # this would die with 'Can't use string ("barf") as a subroutine ref while "strict refs" in use' + } 'double-wrapped doesn\'t die when $_ gets changed'; + is_deeply(\%results, {base=>2,wrapped=>4}); +} diff --git a/xs/Class.xs b/xs/Class.xs deleted file mode 100644 index e187b4d..0000000 --- a/xs/Class.xs +++ /dev/null @@ -1,116 +0,0 @@ -#include "mop.h" - -static void -mop_update_method_map(pTHX_ SV *const self, SV *const class_name, HV *const stash, HV *const map) -{ - const char *const class_name_pv = HvNAME(stash); /* must be HvNAME(stash), not SvPV_nolen_const(class_name) */ - SV *method_metaclass_name; - char *method_name; - I32 method_name_len; - SV *coderef; - HV *symbols; - dSP; - - symbols = mop_get_all_package_symbols(stash, TYPE_FILTER_CODE); - sv_2mortal((SV*)symbols); - (void)hv_iterinit(symbols); - while ( (coderef = hv_iternextsv(symbols, &method_name, &method_name_len)) ) { - CV *cv = (CV *)SvRV(coderef); - char *cvpkg_name; - char *cv_name; - SV *method_slot; - SV *method_object; - - if (!mop_get_code_info(coderef, &cvpkg_name, &cv_name)) { - continue; - } - - /* this checks to see that the subroutine is actually from our package */ - if ( !(strEQ(cvpkg_name, "constant") && strEQ(cv_name, "__ANON__")) ) { - if ( strNE(cvpkg_name, class_name_pv) ) { - continue; - } - } - - method_slot = *hv_fetch(map, method_name, method_name_len, TRUE); - if ( SvOK(method_slot) ) { - SV *const body = mop_call0(aTHX_ method_slot, KEY_FOR(body)); /* $method_object->body() */ - if ( SvROK(body) && ((CV *) SvRV(body)) == cv ) { - continue; - } - } - - method_metaclass_name = mop_call0(aTHX_ self, mop_method_metaclass); /* $self->method_metaclass() */ - - /* - $method_object = $method_metaclass->wrap( - $cv, - associated_metaclass => $self, - package_name => $class_name, - name => $method_name - ); - */ - ENTER; - SAVETMPS; - - PUSHMARK(SP); - EXTEND(SP, 8); - PUSHs(method_metaclass_name); /* invocant */ - mPUSHs(newRV_inc((SV *)cv)); - PUSHs(mop_associated_metaclass); - PUSHs(self); - PUSHs(KEY_FOR(package_name)); - PUSHs(class_name); - PUSHs(KEY_FOR(name)); - mPUSHs(newSVpv(method_name, method_name_len)); - PUTBACK; - - call_sv(mop_wrap, G_SCALAR | G_METHOD); - SPAGAIN; - method_object = POPs; - PUTBACK; - /* $map->{$method_name} = $method_object */ - sv_setsv(method_slot, method_object); - - FREETMPS; - LEAVE; - } -} - -MODULE = Class::MOP::Class PACKAGE = Class::MOP::Class - -PROTOTYPES: DISABLE - -void -get_method_map(self) - SV *self - PREINIT: - HV *const obj = (HV *)SvRV(self); - SV *const class_name = HeVAL( hv_fetch_ent(obj, KEY_FOR(package), 0, HASH_FOR(package)) ); - HV *const stash = gv_stashsv(class_name, 0); - UV current; - SV *cache_flag; - SV *map_ref; - PPCODE: - if (!stash) { - mXPUSHs(newRV_noinc((SV *)newHV())); - return; - } - - current = mop_check_package_cache_flag(aTHX_ stash); - cache_flag = HeVAL( hv_fetch_ent(obj, KEY_FOR(package_cache_flag), TRUE, HASH_FOR(package_cache_flag))); - map_ref = HeVAL( hv_fetch_ent(obj, KEY_FOR(methods), TRUE, HASH_FOR(methods))); - - /* $self->{methods} does not yet exist (or got deleted) */ - if ( !SvROK(map_ref) || SvTYPE(SvRV(map_ref)) != SVt_PVHV ) { - SV *new_map_ref = newRV_noinc((SV *)newHV()); - sv_2mortal(new_map_ref); - sv_setsv(map_ref, new_map_ref); - } - - if ( !SvOK(cache_flag) || SvUV(cache_flag) != current ) { - mop_update_method_map(aTHX_ self, class_name, stash, (HV *)SvRV(map_ref)); - sv_setuv(cache_flag, mop_check_package_cache_flag(aTHX_ stash)); /* update_cache_flag() */ - } - - XPUSHs(map_ref); diff --git a/xs/MOP.xs b/xs/MOP.xs index a699836..e185fa4 100644 --- a/xs/MOP.xs +++ b/xs/MOP.xs @@ -17,7 +17,6 @@ find_method (const char *key, STRLEN keylen, SV *val, void *ud) } EXTERN_C XS(boot_Class__MOP__Package); -EXTERN_C XS(boot_Class__MOP__Class); EXTERN_C XS(boot_Class__MOP__Attribute); EXTERN_C XS(boot_Class__MOP__Method); @@ -34,7 +33,6 @@ BOOT: mop_namespace = newSVpvs("namespace"); MOP_CALL_BOOT (boot_Class__MOP__Package); - MOP_CALL_BOOT (boot_Class__MOP__Class); MOP_CALL_BOOT (boot_Class__MOP__Attribute); MOP_CALL_BOOT (boot_Class__MOP__Method); diff --git a/xs/Package.xs b/xs/Package.xs index 071a0e9..acfeb4c 100644 --- a/xs/Package.xs +++ b/xs/Package.xs @@ -1,7 +1,6 @@ #include "mop.h" - static void mop_deconstruct_variable_name(pTHX_ SV* const variable, const char** const var_name, STRLEN* const var_name_len, @@ -156,6 +155,83 @@ mop_gv_elem(pTHX_ GV* const gv, svtype const type, I32 const add){ } +static void +mop_update_method_map(pTHX_ SV *const self, SV *const class_name, HV *const stash, HV *const map) +{ + const char *const class_name_pv = HvNAME(stash); /* must be HvNAME(stash), not SvPV_nolen_const(class_name) */ + SV *method_metaclass_name; + char *method_name; + I32 method_name_len; + SV *coderef; + HV *symbols; + dSP; + + symbols = mop_get_all_package_symbols(stash, TYPE_FILTER_CODE); + sv_2mortal((SV*)symbols); + (void)hv_iterinit(symbols); + while ( (coderef = hv_iternextsv(symbols, &method_name, &method_name_len)) ) { + CV *cv = (CV *)SvRV(coderef); + char *cvpkg_name; + char *cv_name; + SV *method_slot; + SV *method_object; + + if (!mop_get_code_info(coderef, &cvpkg_name, &cv_name)) { + continue; + } + + /* this checks to see that the subroutine is actually from our package */ + if ( !(strEQ(cvpkg_name, "constant") && strEQ(cv_name, "__ANON__")) ) { + if ( strNE(cvpkg_name, class_name_pv) ) { + continue; + } + } + + method_slot = *hv_fetch(map, method_name, method_name_len, TRUE); + if ( SvOK(method_slot) ) { + SV *const body = mop_call0(aTHX_ method_slot, KEY_FOR(body)); /* $method_object->body() */ + if ( SvROK(body) && ((CV *) SvRV(body)) == cv ) { + continue; + } + } + + method_metaclass_name = mop_call0(aTHX_ self, mop_method_metaclass); /* $self->method_metaclass() */ + + /* + $method_object = $method_metaclass->wrap( + $cv, + associated_metaclass => $self, + package_name => $class_name, + name => $method_name + ); + */ + ENTER; + SAVETMPS; + + PUSHMARK(SP); + EXTEND(SP, 8); + PUSHs(method_metaclass_name); /* invocant */ + mPUSHs(newRV_inc((SV *)cv)); + PUSHs(mop_associated_metaclass); + PUSHs(self); + PUSHs(KEY_FOR(package_name)); + PUSHs(class_name); + PUSHs(KEY_FOR(name)); + mPUSHs(newSVpv(method_name, method_name_len)); + PUTBACK; + + call_sv(mop_wrap, G_SCALAR | G_METHOD); + SPAGAIN; + method_object = POPs; + PUTBACK; + /* $map->{$method_name} = $method_object */ + sv_setsv(method_slot, method_object); + + FREETMPS; + LEAVE; + } +} + MODULE = Class::MOP::Package PACKAGE = Class::MOP::Package PROTOTYPES: DISABLE @@ -191,6 +267,40 @@ get_all_package_symbols(self, filter=TYPE_FILTER_NONE) symbols = mop_get_all_package_symbols(stash, filter); PUSHs(sv_2mortal(newRV_noinc((SV *)symbols))); +void +get_method_map(self) + SV *self + PREINIT: + HV *const obj = (HV *)SvRV(self); + SV *const class_name = HeVAL( hv_fetch_ent(obj, KEY_FOR(package), 0, HASH_FOR(package)) ); + HV *const stash = gv_stashsv(class_name, 0); + UV current; + SV *cache_flag; + SV *map_ref; + PPCODE: + if (!stash) { + mXPUSHs(newRV_noinc((SV *)newHV())); + return; + } + + current = mop_check_package_cache_flag(aTHX_ stash); + cache_flag = HeVAL( hv_fetch_ent(obj, KEY_FOR(package_cache_flag), TRUE, HASH_FOR(package_cache_flag))); + map_ref = HeVAL( hv_fetch_ent(obj, KEY_FOR(methods), TRUE, HASH_FOR(methods))); + + /* $self->{methods} does not yet exist (or got deleted) */ + if ( !SvROK(map_ref) || SvTYPE(SvRV(map_ref)) != SVt_PVHV ) { + SV *new_map_ref = newRV_noinc((SV *)newHV()); + sv_2mortal(new_map_ref); + sv_setsv(map_ref, new_map_ref); + } + + if ( !SvOK(cache_flag) || SvUV(cache_flag) != current ) { + mop_update_method_map(aTHX_ self, class_name, stash, (HV *)SvRV(map_ref)); + sv_setuv(cache_flag, mop_check_package_cache_flag(aTHX_ stash)); /* update_cache_flag() */ + } + + XPUSHs(map_ref); + BOOT: INSTALL_SIMPLE_READER_WITH_KEY(Package, name, package); diff --git a/xt/author/pod_coverage.t b/xt/author/pod_coverage.t index 5f0b6b1..d69fc84 100644 --- a/xt/author/pod_coverage.t +++ b/xt/author/pod_coverage.t @@ -41,7 +41,6 @@ my %trustme = ( 'get_immutable_options', 'reset_package_cache_flag', 'update_package_cache_flag', - 'wrap_method_body', # doc'd with rebless_instance 'rebless_instance_away', @@ -89,6 +88,7 @@ my %trustme = ( ) ], 'Class::MOP::Module' => ['create'], + 'Class::MOP::Package' => ['wrap_method_body'], ); for my $module ( sort @modules ) {