Subject: | Causing Sub::Defer to undefer early breaks some optimizations in Method::Generate::Construtor |
Method::Generate::Constructor will create a constructor which does not call BUILDARGS if it detects that the class' BUILDARGS has been inherited from Moo::Object.
This can be subtly broken by calling Sub::Defer::undefer_all() at a strategic moment. (See attached test case.) It can also be caused by other things that result in constructor getting undeferred early, such as an object instantiation.
As far as I can tell, the only bullet-proof solution is to always call BUILDARGS. This is a shame, because the current optimization does make a measurable difference. Performance could be regained by creating an XS implementation of Moo::Object::BUILDARGS(), and making that an optional (recommended) dependency for Moo.
There may be similar issues around FOREIGNBUILDARGS/BUILDALL/DEMOLISHALL.
Subject: | sub-defer-undefer_all-not-charming-not-delightful.t |
use strictures;
use Test::More;
{
package Rectangle;
use Moo;
has height => (is => "ro", required => 1);
has width => (is => "ro", required => 1);
# Let's assume I actually have a good reason to call
# undefer_all here, and cannot just comment it out.
#
Sub::Defer::undefer_all();
around BUILDARGS => sub {
my ($next, $self) = (shift, shift);
my $params = $self->$next(@_);
$params->{height} //= $params->{width};
$params->{width} //= $params->{height};
return $params;
};
}
is( Rectangle->new(height => 12)->width, 12 );
is( Rectangle->new(width => 12)->height, 12 );
done_testing();