File Coverage

blib/lib/MooseX/XSConstructor.pm
Criterion Covered Total %
statement 87 92 94.5
branch 22 38 57.8
condition 7 18 38.8
subroutine 14 14 100.0
pod 0 3 0.0
total 130 165 78.7


line stmt bran cond sub pod time code
1 1     1   583600 use 5.008008;
  1         6  
2 1     1   7 use strict;
  1         2  
  1         35  
3 1     1   6 use warnings;
  1         2  
  1         104  
4              
5             package MooseX::XSConstructor;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.001002';
9              
10 1     1   7 use Moose 2.2200 ();
  1         38  
  1         29  
11 1     1   6 use Moose::Object ();
  1         2  
  1         31  
12 1     1   7 use Moose::Util ();
  1         2  
  1         21  
13 1     1   807 use Hook::AfterRuntime;
  1         4080  
  1         1286  
14              
15             # Options that either XSCON can handle, or have no effect on the
16             # constructor at all.
17             my $safe_spec = qr/\A(
18             index |
19             is | reader | writer | accessor | predicate | clearer |
20             handles | handles_via | traits |
21             init_arg | required | alias |
22             isa | coerce |
23             builder | default | lazy |
24             trigger |
25             weak_ref |
26             auto_deref |
27             definition_context | associated_class | associated_methods | insertion_order | name | type_constraint |
28             documentation
29             )\z/x;
30              
31             my $safe_traits = qr/\A(
32             MooseX::StrictConstructor::Trait::Class |
33             MooseX::Aliases::Meta::Trait::Class |
34             MooseX::UndefTolerant::Class
35             )\z/x;
36              
37             sub is_suitable_class {
38 1     1 0 5 my ( $self, $klass ) = @_;
39            
40 1         7 my $metaclass = Moose::Util::find_meta( $klass );
41 1 50       65 return unless $metaclass->constructor_class eq 'Moose::Meta::Method::Constructor';
42 1 50       89 return unless $metaclass->destructor_class eq 'Moose::Meta::Method::Destructor';
43 1 50       16 return unless $metaclass->instance_metaclass eq 'Moose::Meta::Instance';
44            
45             my @metaclass_traits =
46 0         0 map { $_->name }
47 1         4 eval { $metaclass->meta->calculate_all_roles };
  1         18  
48 1 50       82 return if grep { $_ !~ $safe_traits } @metaclass_traits;
  0         0  
49            
50 1         5 for my $attr ( $metaclass->get_all_attributes ) {
51            
52 2         81 my @bad = grep { $_ !~ $safe_spec } keys %$attr;
  21         92  
53 2 50       9 return if @bad;
54            
55 2         19 require B;
56 2         13 my $generated = $attr->_inline_instance_set(q{$XXX}, q{$YYY});
57 2         79 my $expected = sprintf q{%s->{%s} = %s}, q{$XXX}, B::perlstring($attr->name), q{$YYY};
58 2 50       11 return if $generated ne $expected;
59             }
60            
61 1         6 return "I assume so";
62             }
63              
64             sub setup_for {
65 1     1 0 21 my ( $self, $klass ) = @_;
66            
67 1 50       6 return unless $self->is_suitable_class( $klass );
68            
69 1         6 my $metaclass = Moose::Util::find_meta( $klass );
70            
71             # Transform it into arguments which XSCON can handle
72             my @args =
73             map {
74 1         13 my $attr = $_;
  2         66  
75 2         8 my $slot = $attr->name;
76 2         6 my %xs_spec = ();
77 2 100       134 $xs_spec{required} = 1 if $attr->is_required;
78 2 50       125 $xs_spec{weak_ref} = 1 if $attr->is_weak_ref;
79 2 50 33     27 $xs_spec{default} = $attr->default if $attr->has_default && !$attr->is_lazy;
80 2 100 66     20 $xs_spec{builder} = $attr->builder if $attr->has_builder && !$attr->is_lazy;
81 2         90 $xs_spec{init_arg} = $attr->init_arg;
82 2 50       145 $xs_spec{trigger} = $attr->trigger if $attr->has_trigger;
83 2 100       148 if ( $attr->has_type_constraint ) {
84 1         53 $xs_spec{isa} = $attr->type_constraint;
85 1         64 $xs_spec{coerce} = !!$attr->should_coerce;
86             }
87 2 50       24 $xs_spec{undef_tolerant} = 1
88             if Moose::Util::does_role( $attr, 'MooseX::UndefTolerant::Attribute' );
89 2 50 33     219 $xs_spec{alias} = $attr->alias
90             if $attr->can('has_alias') && $attr->has_alias;
91 2         10 ( $slot => \%xs_spec );
92             }
93             $metaclass->get_all_attributes;
94            
95 1 50       5 Moose::Util::does_role( $metaclass, 'MooseX::StrictConstructor::Trait::Class' )
96             and push @args, '!!';
97            
98             # Keep track of old constructor, just in case.
99 1         75 my $old = $klass->can( 'new' );
100            
101             # Call XSCON to replace the existing constructor method
102 1         4 my $ok = eval {
103 1         904 require Class::XSConstructor;
104 1         181459 Class::XSConstructor->VERSION( 0.018001 );
105 1         5 local $Class::XSConstructor::REDEFINE = !!1;
106 1         8 Class::XSConstructor->import( [ $klass, 'new' ], @args );
107 1         482 1;
108             };
109            
110 1 50 33     9 if ( $ok and my $meta = Class::XSConstructor::get_metadata( $klass ) ) {
111 1 50 33     38 if ( defined $meta->{foreignclass}
      33        
112             and ( $meta->{foreignclass} eq 'Moose::Object' or $self->is_suitable_class( $meta->{foreignclass} ) ) ) {
113 1         7 delete $meta->{$_} for qw/
114             foreignbuildall
115             foreignconstructor
116             foreignclass
117             foreignbuildargs
118             /;
119             }
120 1 50       6 if ( $meta->{buildargs} == \&Moose::Object::BUILDARGS ) {
121 1         3 $meta->{has_standard_buildargs} = !!1;
122 1         16 delete $meta->{buildargs};
123             }
124 1         5 $klass->XSCON_CLEAR_CONSTRUCTOR_CACHE;
125             }
126             else {
127             # If there was a failure, restore old constructor.
128 1     1   11 no strict 'refs';
  1         3  
  1         56  
129 1     1   6 no warnings 'redefine';
  1         2  
  1         433  
130 0         0 *{"${klass}::new"} = $old;
  0         0  
131 0         0 return;
132             }
133            
134 1         663 require Class::XSDestructor;
135 1         599 local $Class::XSDestructor::REDEFINE = !!1;
136 1         9 Class::XSDestructor->import( [ $klass, 'DESTROY' ] );
137            
138 1         82 return $klass;
139             }
140              
141             sub import {
142 1     1   12 my $self = shift;
143 1         3 my $caller = caller;
144 1     1   6 after_runtime { $self->setup_for( $caller ) };
  1         299655  
145             }
146              
147             sub is_xs {
148 8     8 0 34350 require B;
149 8         101 !! B::svref_2object( shift )->XSUB;
150             }
151              
152             __PACKAGE__
153             __END__
154              
155             =pod
156              
157             =encoding utf-8
158              
159             =head1 NAME
160              
161             MooseX::XSConstructor - glue between Moose and Class::XSConstructor
162              
163             =head1 SYNOPSIS
164              
165             package Foo;
166            
167             use Moose;
168             use MooseX::XSConstructor;
169            
170             ...; # Normal Moose stuff
171            
172             __PACKAGE__->meta->make_immutable(
173             inline_constructor => 0,
174             inline_destructor => 0,
175             );
176              
177             =head1 DESCRIPTION
178              
179             This module speeds up all your Mooses. (Meese?)
180              
181             It does this by replacing the normal Perl constructor that Moose
182             generates for your class with a faster one written in XS.
183              
184             If it detects that your class cannot be accellerated, then it will
185             bail out and do nothing.
186              
187             Most built-in Moose features are supported though, as are a few
188             extensions. Namely: L<MooseX::Aliases>, L<MooseX::StrictConstructor>,
189             and L<MooseX::UndefTolerant>. If you're using other MooseX modules,
190             you probably won't get a speedup.
191              
192             =head1 BUGS
193              
194             Please report any bugs to
195             L<https://github.com/tobyink/p5-moosex-xsconstructor/issues>.
196              
197             =head1 SEE ALSO
198              
199             L<MooX::XSConstructor>,
200             L<Class::XSConstructor>.
201              
202             =head1 AUTHOR
203              
204             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
205              
206             =head1 COPYRIGHT AND LICENCE
207              
208             This software is copyright (c) 2026 by Toby Inkster.
209              
210             This is free software; you can redistribute it and/or modify it under
211             the same terms as the Perl 5 programming language system itself.
212              
213             =head1 DISCLAIMER OF WARRANTIES
214              
215             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
216             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
217             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
218