File Coverage

blib/lib/mop.pm
Criterion Covered Total %
statement 186 191 97.3
branch 24 32 75.0
condition 6 14 42.8
subroutine 34 34 100.0
pod 10 10 100.0
total 260 281 92.5


line stmt bran cond sub pod time code
1             package mop;
2              
3 143     143   4160321 use v5.16;
  143         562  
  143         6052  
4 143     143   185962 use mro;
  143         157237  
  143         863  
5 143     143   4426 use warnings;
  143         295  
  143         3325  
6              
7 143     143   272830 use overload ();
  143         168068  
  143         3157  
8 143     143   1027 use Scalar::Util ();
  143         267  
  143         7046  
9              
10             our $VERSION = '0.03';
11             our $AUTHORITY = 'cpan:STEVAN';
12              
13             our $BOOTSTRAPPED = 0;
14              
15 143     143   21186 use XSLoader;
  143         266  
  143         12889  
16             XSLoader::load(__PACKAGE__, $VERSION);
17              
18 143     143   82644 use mop::object;
  143         470  
  143         4287  
19 143     143   97693 use mop::class;
  143         487  
  143         5255  
20 143     143   102901 use mop::method;
  143         471  
  143         4091  
21 143     143   112345 use mop::attribute;
  143         441  
  143         4559  
22              
23 143     143   1046 use mop::internals::observable;
  143         288  
  143         3313  
24              
25 143     143   84299 use mop::internals::syntax;
  143         479  
  143         5466  
26 143     143   7532 use mop::internals::util;
  143         320  
  143         3897  
27              
28 143     143   115337 use mop::traits;
  143         414  
  143         7885  
29 143     143   108759 use mop::traits::util;
  143         395  
  143         363938  
30              
31             $Carp::Internal{$_}++ for qw/
32             op
33             mop
34             mop::attribute
35             mop::class
36             mop::internals::observable
37             mop::internals::syntax
38             mop::internals::util
39             mop::method
40             mop::object
41             mop::role
42             mop::traits
43             mop::traits::util
44             /;
45              
46             sub import {
47 168     168   16372 shift;
48 168         510 my $pkg = caller;
49 168         466 my %opts = @_;
50              
51 168         574 initialize();
52 168         1042 mop::internals::syntax::setup_for($pkg);
53 168         953 mop::traits::setup_for($pkg);
54              
55             # NOTE: don't allow setting attribute or method metaclasses here, because
56             # that is controlled by the class or role metaclass via method_class and
57             # attribute_class.
58 168         480 for my $type (qw(class role)) {
59 336 100       46496 if (defined(my $meta = $opts{"${type}_metaclass"})) {
60 2         859 require(($meta =~ s{::}{/}gr) . '.pm');
61 2         116 $^H{"mop/default_${type}_metaclass"} = $meta;
62             }
63             }
64             }
65              
66             sub unimport {
67 6     6   249 my $pkg = caller;
68 6         25 mop::internals::syntax::teardown_for($pkg);
69 6         32 mop::traits::teardown_for($pkg);
70             }
71              
72             sub meta {
73 12933   66 12933 1 105513 my $pkg = ref($_[0]) || $_[0];
74 12933         67902 mop::internals::util::get_meta($pkg);
75             }
76              
77             sub remove_meta {
78 149   33 149 1 1011 my $pkg = ref($_[0]) || $_[0];
79 149         1080 mop::internals::util::unset_meta($pkg);
80             }
81              
82 23291     23291 1 127136 sub id { Hash::Util::FieldHash::id( $_[0] ) }
83              
84             sub is_mop_object {
85 12     12 1 31 defined Hash::Util::FieldHash::id_2obj( id( $_[0] ) );
86             }
87              
88             sub apply_metaclass {
89             # TODO: we should really not be calling apply_metaclass at all during
90             # bootstrapping, but it's done in a couple places for simplicity, to avoid
91             # needing multiple implementations of things for pre- and
92             # post-bootstrapping. we should probably eventually actually do the
93             # replacement in those methods, to make sure bootstrapping isn't doing
94             # unnecessary extra work. the actual implementation is replaced below.
95 1     1 1 3 return;
96             }
97              
98             sub apply_metarole {
99 1     1 1 5 my ($instance, $new_metarole) = @_;
100              
101 1         3 my $meta = mop::meta($instance);
102 1         5 my $meta_name = Scalar::Util::blessed($meta);
103 1   33     7 my $role_name = Scalar::Util::blessed($new_metarole) // $new_metarole;
104 1         3 my $metarole = mop::meta($role_name);
105              
106 1 50       9 die "Could not find metaclass for role: $_"
107             unless $metarole;
108              
109 1         4 my $new_meta_name = "mop::metarole::${meta_name}::${role_name}";
110 1         2 my $new_meta;
111 1 50       2 if (!($new_meta = mop::meta($new_meta_name))) {
112 1         4 $new_meta = $meta_name->new(
113             name => $new_meta_name,
114             superclass => $meta->name,
115             roles => [$metarole],
116             );
117 1         4 $new_meta->FINALIZE;
118             }
119              
120 1         4 apply_metaclass($instance, $new_meta->name);
121             }
122              
123             sub rebless {
124 453     453 1 1217 my ($object, $into) = @_;
125              
126 453         1657 my $from = Scalar::Util::blessed($object);
127 453         1722 my $common_base = mop::internals::util::find_common_base($from, $into);
128              
129 453         745 my @from_isa = @{ mro::get_linear_isa($from) };
  453         2508  
130 453 100       1274 if ($common_base) {
131 452         2734 pop @from_isa until $from_isa[-1] eq $common_base;
132 452         671 pop @from_isa;
133             }
134 453         1067 @from_isa = grep { defined } map { meta($_) } @from_isa;
  9         30  
  9         23  
135              
136 453         669 my @into_isa = @{ mro::get_linear_isa($into) };
  453         1980  
137 453 100       1406 if ($common_base) {
138 452         1821 pop @into_isa until $into_isa[-1] eq $common_base;
139 452         816 pop @into_isa;
140             }
141 453         5715 @into_isa = grep { defined } map { meta($_) } @into_isa;
  50         183  
  50         157  
142              
143 453         2875 for my $attr (map { $_->attributes } @from_isa) {
  8         38  
144 8         32 $attr->store_data_in_slot_for($object, undef);
145             }
146              
147 453         1185 bless($object, $into);
148              
149 453         1301 for my $attr (map { $_->attributes } reverse @into_isa) {
  50         248  
150 14         44 $attr->store_default_in_slot_for($object);
151             }
152              
153             $object
154 453         2106 }
155              
156             sub dump_object {
157 12     12 1 59 my ($obj) = @_;
158              
159 12 100       31 return $obj unless is_mop_object($obj);
160              
161 11         22 our %SEEN;
162 11 50       19 if ($SEEN{id($obj)}) {
163 0         0 return '';
164             }
165 11   50     20 local $SEEN{id($obj)} = ($SEEN{id($obj)} // 0) + 1;
166              
167             my %attributes = map {
168 24 50       40 if (my $m = meta($_)) {
  11         40  
169 24         23 %{ $m->attribute_map }
  24         113  
170             }
171 11         20 } reverse @{ mro::get_linear_isa(ref $obj) };
172              
173 11         30 my $temp = {
174             __ID__ => id($obj),
175             __CLASS__ => meta($obj)->name,
176             __SELF__ => $obj,
177             };
178              
179 11         28 foreach my $attr (values %attributes) {
180 19 50 33     133 if ($obj->isa('mop::attribute') && $attr->name eq '$!storage') {
181 0         0 $temp->{ $attr->name } = '__INTERNAL_DETAILS__';
182             } else {
183             $temp->{ $attr->name } = sub {
184 19     19   31 my ($data) = @_;
185 19 100       68 if (Scalar::Util::blessed($data)) {
    100          
186 4         21 return dump_object($data);
187             } elsif (ref $data) {
188 1 50       8 if (ref $data eq 'ARRAY') {
    50          
189 0         0 return [ map { __SUB__->( $_ ) } @$data ];
  0         0  
190             } elsif (ref $data eq 'HASH') {
191             return {
192 1         13 map { $_ => __SUB__->( $data->{$_} ) } keys %$data
  1         66  
193             };
194             } else {
195 0         0 return $data;
196             }
197             } else {
198 14         44 return $data;
199             }
200 19         33 }->(${ $attr->get_slot_for($obj) });
  19         46  
201             }
202             }
203              
204 9         43 $temp;
205             }
206              
207             # can't call this 'bootstrap' because XSLoader has a special meaning for that
208             sub initialize {
209 169 100   169 1 854 return if $BOOTSTRAPPED;
210 143         1333 mop::internals::util::set_meta($_, $_->__INIT_METACLASS__) for qw[
211             mop::object
212             mop::role
213             mop::class
214             mop::attribute
215             mop::method
216             mop::internals::observable
217             ];
218              
219 143         705 my $Object = meta('mop::object');
220              
221 143         489 my $Role = meta('mop::role');
222 143         492 my $Class = meta('mop::class');
223              
224 143         519 my $Method = meta('mop::method');
225 143         460 my $Attribute = meta('mop::attribute');
226 143         451 my $Observable = meta('mop::internals::observable');
227              
228             # flatten mop::observable into wherever it's needed (it's just an
229             # implementation detail (#95), so it shouldn't end up being directly
230             # visible)
231 143         489 foreach my $meta ( $Role, $Attribute, $Method ) {
232 429         1875 for my $attribute ( $Observable->attributes ) {
233 429         1833 $meta->add_attribute($attribute->clone(associated_meta => $meta));
234             }
235 429         1755 for my $method ( $Observable->methods ) {
236 1716         4810 $meta->add_method($method->clone(associated_meta => $meta));
237             }
238             }
239              
240             # At this point the metaclass
241             # layer class to role relationship
242             # is correct. And the following
243             # - Class does Role
244             # - Role is instance of Class
245             # - Role does Role
246             # is true.
247 143         1742 $Class->add_role( $Role );
248              
249             # normally this would be a call to FINALIZE for all of the mop classes,
250             # but that complicates things too much during bootstrapping, and this
251             # is the only thing that would have an actual effect anyway.
252 143         735 mop::internals::util::apply_all_roles($Class, $Role);
253              
254             # and now this is no longer needed
255 143         894 remove_meta('mop::internals::observable');
256              
257             {
258             # NOTE:
259             # This is ugly, but we need to do
260             # it to set the record straight
261             # and make sure that the relationship
262             # between mop::class and mop::role
263             # are correct and code is reused.
264             # - SL
265 143         252 foreach my $method ($Role->methods) {
  143         703  
266 143     143   1340 no strict 'refs';
  143         301  
  143         22356  
267 4433         9432 *{ 'mop::class::' . $method->name } = $method->body
  4576         10180  
268 4576 100       5160 unless defined &{ 'mop::class::' . $method->name };
269             }
270              
271             # now make sure the Observable roles are
272             # completely intergrated into the stashes
273 143         904 foreach my $method ($Observable->methods) {
274 572         1448 foreach my $package (qw(mop::role mop::method mop::attribute)) {
275 143     143   821 no strict 'refs';
  143         315  
  143         24945  
276 1716         4800 *{ $package . '::' . $method->name } = $method->body
  1716         4769  
277 1716 50       1991 unless defined &{ $package . '::' . $method->name };
278             }
279             }
280              
281             # then clean up some of the @ISA by
282             # removing mop::observable from them
283 143         5270 @mop::role::ISA = ('mop::object');
284 143         2629 @mop::method::ISA = ('mop::object');
285 143         2353 @mop::attribute::ISA = ('mop::object');
286              
287             # Here we finalize the rest of the
288             # metaclass layer so that the following:
289             # - Class is an instance of Class
290             # - Object is an instance of Class
291             # - Class is a subclass of Object
292             # is true.
293 143         2322 @mop::class::ISA = ('mop::object');
294              
295             # remove the temporary clone methods used in the bootstrap
296 143         1796 delete $mop::method::{clone};
297 143         1522 delete $mop::attribute::{clone};
298              
299             # replace the temporary implementation of mop::object::new
300             {
301 143     143   837 no strict 'refs';
  143         561  
  143         10740  
  143         275  
302 143     143   857 no warnings 'redefine';
  143         287  
  143         15536  
303 143         682 *{ 'mop::object::new' } = $Object->get_method('new')->body;
  143         1204  
304             }
305              
306             # remove the temporary constructors used in the bootstrap
307 143         944 delete $mop::class::{new};
308 143         851 delete $mop::role::{new};
309 143         1729 delete $mop::method::{new};
310 143         1894 delete $mop::attribute::{new};
311             }
312              
313             {
314 143     143   744 no warnings 'redefine';
  143         260  
  143         29599  
  143         385  
315             *apply_metaclass = mop::internals::util::subname(
316             apply_metaclass => sub {
317 447     447 1 1970 my ($instance, $new_meta) = @_;
        447      
318 447         1847 rebless $instance, mop::internals::util::fix_metaclass_compatibility($new_meta, $instance);
319             }
320 143         5982 );
321             }
322              
323 143         578 $BOOTSTRAPPED = 1;
324             }
325              
326             1;
327              
328             __END__