Merge branch 'master' into topic/symbol-manipulator
gfx [Sun, 16 Aug 2009 01:51:35 +0000 (10:51 +0900)]
Conflicts:
lib/Class/MOP/Class.pm
xs/Package.xs

32 files changed:
Changes
Makefile.PL
README
bench/loading-benchmark.pl
lib/Class/MOP.pm
lib/Class/MOP/Attribute.pm
lib/Class/MOP/Class.pm
lib/Class/MOP/Class/Immutable/Trait.pm
lib/Class/MOP/Instance.pm
lib/Class/MOP/Method.pm
lib/Class/MOP/Method/Accessor.pm
lib/Class/MOP/Method/Constructor.pm
lib/Class/MOP/Method/Generated.pm
lib/Class/MOP/Method/Inlined.pm
lib/Class/MOP/Method/Wrapped.pm
lib/Class/MOP/Module.pm
lib/Class/MOP/Object.pm
lib/Class/MOP/Package.pm
lib/metaclass.pm
mop.c
ppport.h
t/005_attributes.t
t/010_self_introspection.t
t/021_attribute_errors_and_edge_cases.t
t/030_method.t
t/049_metaclass_reinitialize.t [new file with mode: 0644]
t/061_instance_inline.t
t/313_before_after_dollar_under.t [new file with mode: 0644]
xs/Class.xs [deleted file]
xs/MOP.xs
xs/Package.xs
xt/author/pod_coverage.t

diff --git a/Changes b/Changes
index 8d6bcff..f7d39fb 100644 (file)
--- 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
index 8610c40..a872582 100644 (file)
@@ -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 (file)
--- 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
 
index 2994f6c..dc0184c 100755 (executable)
@@ -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;
     },
 };
index cf80489..8f2f6ac 100644 (file)
@@ -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
index e605533..8f30cc2 100644 (file)
@@ -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
index fbceff6..d35f33e 100644 (file)
@@ -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<sub
-Package::name { ... }>) will be included. Similarly, methods named
-with a fully qualified name using L<Sub::Name> 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<Class::MOP::Package/Method introspection and creation> 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<Class::MOP::Method> for the specified
-C<$method_name>. If the class does not have the specified method, it
-returns C<undef>
-
-=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<Class::MOP::Method> objects.
-
-=item B<< $metaclass->get_method_list >>
-
-This will return a list of method I<names> 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<SUPER::$method_name>
 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<Class::MOP::Method>, 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<Class::MOP::Method> object for the method.
-
-=item B<< $metaclass->method_metaclass >>
-
-Returns the class name of the method metaclass, see
-L<Class::MOP::Method> for more information on the method metaclass.
-
-=item B<< $metaclass->wrapped_method_metaclass >>
-
-Returns the class name of the wrapped method metaclass, see
-L<Class::MOP::Method::Wrapped> for more information on the wrapped
-method metaclass.
-
 =back
 
 =head2 Attribute introspection and creation
index bb1b7e1..29c79e8 100644 (file)
@@ -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';
 
index b617afa..fc418d8 100644 (file)
@@ -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 {
index 3b6d025..9a0cdda 100644 (file)
@@ -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<Class::MOP::Method> 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
 
index 59eb20c..a92b5bc 100644 (file)
@@ -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';
 
index c1ac2b3..e145a48 100644 (file)
@@ -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';
 
index a1e9632..1481e96 100644 (file)
@@ -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;
         $@;
     };
 
index 26b4887..fa68ebe 100644 (file)
@@ -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';
 
index 4e72a59..0f75ced 100644 (file)
@@ -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,
index d233ec3..e7ce4d1 100644 (file)
@@ -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';
 
index 93649bc..c11ca0e 100644 (file)
@@ -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';
 
index 38269d2..f2c8e44 100644 (file)
@@ -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<Class::MOP::Package> 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<initialize>
+before calling C<initialize>. In contrast to C<initialize>, you may
+also pass an existing C<Class::MOP::Package> 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<list_all_package_symbols>, 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<sub
+Package::name { ... }>) will be included. Similarly, methods named
+with a fully qualified name using L<Sub::Name> 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<Class::MOP::Method> for the specified
+C<$method_name>. If the class does not have the specified method, it
+returns C<undef>
+
+=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<Class::MOP::Method> objects.
+
+=item B<< $metapackage->get_method_list >>
+
+This will return a list of method I<names> 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<Class::MOP::Method>, 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<Class::MOP::Method> object for the method.
+
+=item B<< $metapackage->method_metaclass >>
+
+Returns the class name of the method metaclass, see
+L<Class::MOP::Method> for more information on the method metaclass.
+
+=item B<< $metapackage->wrapped_method_metaclass >>
+
+Returns the class name of the wrapped method metaclass, see
+L<Class::MOP::Method::Wrapped> for more information on the wrapped
+method metaclass.
+
 =item B<< Class::MOP::Package->meta >>
 
 This will return a L<Class::MOP::Class> instance for this class.
index 62c6d8b..8ea3c67 100644 (file)
@@ -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 (file)
--- 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;
 }
index 3e3d52f..ec2f1cc 100644 (file)
--- 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
index 1429a0a..b7a545b 100644 (file)
@@ -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();
 }
index 04504f4..bcc6335 100644 (file)
@@ -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');
index 4de3ff0..b3da244 100644 (file)
@@ -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';
 }
index e0dbe62..71cc17d 100644 (file)
@@ -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 (file)
index 0000000..a9c0e26
--- /dev/null
@@ -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;
index 3856bfe..0141945 100644 (file)
@@ -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 (file)
index 0000000..f029173
--- /dev/null
@@ -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 (file)
index e187b4d..0000000
+++ /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);
index a699836..e185fa4 100644 (file)
--- 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);
 
index 071a0e9..acfeb4c 100644 (file)
@@ -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);
 
index 5f0b6b1..d69fc84 100644 (file)
@@ -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 ) {