MultiMethodContainer.nqp (rakudo-2020.09) | : | MultiMethodContainer.nqp (rakudo-2020.10) | ||
---|---|---|---|---|
role Perl6::Metamodel::MultiMethodContainer { | role Perl6::Metamodel::MultiMethodContainer { | |||
# Set of multi-methods to incorporate. Not just the method handles; | # Set of multi-methods to incorporate. Not just the method handles; | |||
# each is a hash containing keys name and body. | # each is a hash containing keys name and body. | |||
has @!multi_methods_to_incorporate; | has @!multi_methods_to_incorporate; | |||
has %!multi_candidate_names; | has %!multi_candidate_names; | |||
# The proto we'll clone. | # The proto we'll clone. | |||
my $autogen_proto; | my $autogen_method_proto; | |||
my $autogen_submethod_proto; | ||||
# Sets the proto we'll auto-gen based on. | # Sets the proto we'll auto-gen based on. | |||
method set_autogen_proto($proto) { | method set_autogen_proto($method_proto, $submethod_proto) { | |||
$autogen_proto := $proto | $autogen_method_proto := $method_proto; | |||
$autogen_submethod_proto := $submethod_proto; | ||||
} | } | |||
# We can't incorporate multis right away as we don't know all parents | # We can't incorporate multis right away as we don't know all parents | |||
# yet, maybe, which influences whether we even can have multis, need to | # yet, maybe, which influences whether we even can have multis, need to | |||
# generate a proto and so forth. So just queue them up in a todo list and | # generate a proto and so forth. So just queue them up in a todo list and | |||
# we handle it at class composition time. | # we handle it at class composition time. | |||
method add_multi_method($obj, $name, $code_obj) { | method add_multi_method($obj, $name, $code_obj) { | |||
# Represents a multi candidate to incorporate. | # Represents a multi candidate to incorporate. | |||
my class MultiToIncorporate { | my class MultiToIncorporate { | |||
has $!name; | has $!name; | |||
skipping to change at line 44 | skipping to change at line 46 | |||
# Gets the multi methods that are to be incorporated. | # Gets the multi methods that are to be incorporated. | |||
method multi_methods_to_incorporate($obj) { | method multi_methods_to_incorporate($obj) { | |||
@!multi_methods_to_incorporate | @!multi_methods_to_incorporate | |||
} | } | |||
# Incorporates the multi candidates into the appropriate proto. Need to | # Incorporates the multi candidates into the appropriate proto. Need to | |||
# implement proto incorporation yet. | # implement proto incorporation yet. | |||
method incorporate_multi_candidates($obj) { | method incorporate_multi_candidates($obj) { | |||
my $num_todo := +@!multi_methods_to_incorporate; | my $num_todo := +@!multi_methods_to_incorporate; | |||
my $i := 0; | my $i := 0; | |||
my $submethod_type := Perl6::Metamodel::Configuration.submethod_type; | ||||
my @new_protos; | my @new_protos; | |||
while $i != $num_todo { | while $i != $num_todo { | |||
# Get method name and code. | # Get method name and code. | |||
my $name := @!multi_methods_to_incorporate[$i].name; | my $name := @!multi_methods_to_incorporate[$i].name; | |||
my $code := @!multi_methods_to_incorporate[$i].code; | my $code := @!multi_methods_to_incorporate[$i].code; | |||
# Do we have anything in the methods table already in | # Do we have anything in the methods table already in | |||
# this class? | # this class? | |||
my %meths := nqp::hllize(self.method_table($obj)); | my $is_submethod := nqp::istype(nqp::what($code), $submethod_type); | |||
my $method_table := $is_submethod | ||||
?? 'submethod_table' | ||||
!! 'method_table'; | ||||
my $autogen_proto := $is_submethod | ||||
?? $autogen_submethod_proto | ||||
!! $autogen_method_proto; | ||||
my %meths := nqp::hllize(self."$method_table"($obj)); | ||||
if nqp::existskey(%meths, $name) { | if nqp::existskey(%meths, $name) { | |||
# Yes. Only or dispatcher, though? If only, error. If | # Yes. Only or dispatcher, though? If only, error. If | |||
# dispatcher, simply add new dispatchee. | # dispatcher, simply add new dispatchee. | |||
my $dispatcher := %meths{$name}; | my $dispatcher := %meths{$name}; | |||
if $dispatcher.is_dispatcher { | if $dispatcher.is_dispatcher { | |||
$dispatcher.add_dispatchee($code); | $dispatcher.add_dispatchee($code); | |||
} | } | |||
else { | else { | |||
nqp::die("Cannot have a multi candidate for '" ~ $name ~ | nqp::die("Cannot have a multi candidate for '" ~ $name ~ | |||
"' when an only method is also in the package '" ~ | "' when an only method is also in the package '" ~ | |||
self.name($obj) ~ "'"); | self.name($obj) ~ "'"); | |||
} | } | |||
} | } | |||
else { | else { | |||
# Go hunting in the MRO for a proto. | ||||
my @mro := self.mro($obj); | ||||
my $j := 1; | ||||
my $found := 0; | my $found := 0; | |||
while $j != +@mro && !$found { | unless $is_submethod { | |||
my $parent := @mro[$j]; | # Go hunting in the MRO for a method proto. Note that we don | |||
my %meths := nqp::hllize($parent.HOW.method_table($parent)); | 't traverse MRO for submethods. | |||
if nqp::existskey(%meths, $name) { | my @mro := self.mro($obj); | |||
# Found a possible - make sure it's a dispatcher, not | my $j := 1; | |||
# an only. | while $j != +@mro && !$found { | |||
my $dispatcher := %meths{$name}; | my $parent := @mro[$j]; | |||
if $dispatcher.is_dispatcher { | my %meths := nqp::hllize($parent.HOW."$method_table"($pa | |||
# Clone it and install it in our method table. | rent)); | |||
my $copy := $dispatcher.derive_dispatcher(); | if nqp::existskey(%meths, $name) { | |||
$copy.add_dispatchee($code); | # Found a possible - make sure it's a dispatcher, no | |||
self.add_method($obj, $name, $copy); | t | |||
nqp::push(@new_protos, $copy); | # an only. | |||
$found := 1; | my $dispatcher := %meths{$name}; | |||
if $dispatcher.is_dispatcher { | ||||
# Clone it and install it in our method table. | ||||
my $copy := $dispatcher.derive_dispatcher(); | ||||
$copy.add_dispatchee($code); | ||||
self.add_method($obj, $name, $copy); | ||||
nqp::push(@new_protos, $copy); | ||||
$found := 1; | ||||
} | ||||
} | } | |||
$j := $j + 1; | ||||
} | } | |||
$j := $j + 1; | ||||
} | } | |||
unless $found { | unless $found { | |||
# No proto found, so we'll generate one here. | # No proto found, so we'll generate one here. | |||
unless $autogen_proto { | unless $autogen_proto { | |||
nqp::die("Cannot auto-generate a proto method for '$name ' in the setting"); | nqp::die("Cannot auto-generate a proto method for '$name ' in the setting"); | |||
} | } | |||
my $proto := $autogen_proto.instantiate_generic( | my $proto := $autogen_proto.instantiate_generic( | |||
nqp::hash('T', $obj)); | nqp::hash('T', $obj)); | |||
$proto.set_name($name); | $proto.set_name($name); | |||
$proto.add_dispatchee($code); | $proto.add_dispatchee($code); | |||
End of changes. 8 change blocks. | ||||
22 lines changed or deleted | 37 lines changed or added |