Skip to content
Permalink
Browse files

Fix bogus role conflict when the same method is added under multiple …

…names

It is possible to add the same method object under different names to a class
or role. Thus we must not rely on the method object's name but always use the
name given to add_method when processing lists of methods.

Many thanks to Xliff++ for the excellent report! Fixes GH #2873
  • Loading branch information...
niner committed May 4, 2019
1 parent 37b7ef7 commit e274f4a26ed0323369937fc867871d644173fe54
@@ -5,6 +5,7 @@ role Perl6::Metamodel::MethodContainer {

# The order that the methods were added in.
has @!method_order;
has @!method_names;

# Cache that expires when we add methods (primarily to support NFA stuff).
# The hash here is readonly; we copy/replace in on addition, for thread
@@ -39,6 +40,7 @@ role Perl6::Metamodel::MethodContainer {
nqp::setmethcacheauth($obj, 0);
%!cache := {};
@!method_order[+@!method_order] := $code_obj;
@!method_names[+@!method_names] := $name;
}

# Gets the method hierarchy.
@@ -65,6 +67,14 @@ role Perl6::Metamodel::MethodContainer {
@meths
}

method method_order($obj) {
@!method_order
}

method method_names($obj) {
@!method_names
}

# Get the method table. Only contains methods directly declared here,
# and excludes submethods.
method method_table($obj) {
@@ -176,13 +176,16 @@ class Perl6::Metamodel::ParametricRoleHOW

# Go through methods and instantiate them; we always do this
# unconditionally, since we need the clone anyway.
my @methods := nqp::hllize(self.methods($obj, :local(1)));
for @methods -> $method {
$conc.HOW.add_method($conc, $method.name, $method.instantiate_generic($type_env))
}
my @private_methods := nqp::hllize(self.private_methods($obj));
for @private_methods -> $method {
$conc.HOW.add_private_method($conc, $method.name, $method.instantiate_generic($type_env));
my @methods := nqp::hllize(self.method_order($obj));
my @method_names := nqp::hllize(self.method_names($obj));
my $method_iterator := nqp::iterator(@methods);
for @method_names -> $name {
$conc.HOW.add_method($conc, $name, nqp::shift($method_iterator).instantiate_generic($type_env))
}
my %private_methods := nqp::hllize(self.private_method_table($obj));
my @private_methods := nqp::hllize(self.private_method_names($obj));
for @private_methods -> $name {
$conc.HOW.add_private_method($conc, $name, %private_methods{$name}.instantiate_generic($type_env));
}
for self.multi_methods_to_incorporate($obj) {
$conc.HOW.add_multi_method($conc, $_.name, $_.code.instantiate_generic($type_env))
@@ -1,6 +1,7 @@
role Perl6::Metamodel::PrivateMethodContainer {
has %!private_methods;
has @!private_methods;
has @!private_method_names;

# Adds a private method.
method add_private_method($obj, $name, $code) {
@@ -10,6 +11,7 @@ role Perl6::Metamodel::PrivateMethodContainer {
}
%!private_methods{$name} := $code;
nqp::push(@!private_methods, $code);
nqp::push(@!private_method_names, $name);
}

# Gets the table of private methods.
@@ -21,6 +23,10 @@ role Perl6::Metamodel::PrivateMethodContainer {
@!private_methods
}

method private_method_names($obj) {
@!private_method_names
}

# Locates a private method, and hands back null if it doesn't exist.
method find_private_method($obj, $name) {
nqp::existskey(%!private_methods, $name) ??
@@ -102,9 +102,10 @@ my class RoleToClassApplier {
my @stubs;

# Compose in any methods.
sub compose_method_table(@methods) {
for @methods -> $method {
my str $name := $method.name;
sub compose_method_table(@methods, @method_names) {
my $method_iterator := nqp::iterator(@methods);
for @method_names -> str $name {
my $method := nqp::shift($method_iterator);
my $yada := 0;
try { $yada := $method.yada }
if $yada {
@@ -126,14 +127,21 @@ my class RoleToClassApplier {
}
}
}
compose_method_table(nqp::hllize($to_compose_meta.methods($to_compose, :local(1))));
my @methods := $to_compose_meta.method_order($to_compose);
my @method_names := $to_compose_meta.method_names($to_compose);
compose_method_table(
nqp::hllize(@methods),
nqp::hllize(@method_names),
);
if nqp::can($to_compose_meta, 'private_method_table') {
my @private_methods := nqp::hllize($to_compose_meta.private_methods($to_compose));
for @private_methods -> $method {
my str $name := $method.name;
my @private_methods := nqp::hllize($to_compose_meta.private_methods($to_compose));
my @private_method_names := nqp::hllize($to_compose_meta.private_method_names($to_compose));
my $i := 0;
for @private_method_names -> str $name {
unless has_private_method($target, $name) {
$target.HOW.add_private_method($target, $name, $method);
$target.HOW.add_private_method($target, $name, @private_methods[$i]);
}
$i++;
}
}

@@ -15,9 +15,10 @@ my class RoleToRoleApplier {
my %priv_meth_providers;
for @roles {
my $role := $_;
sub build_meth_info(@methods, %meth_info_to_use, @meth_names_to_use, %meth_providers_to_use) {
for @methods -> $meth {
my $name := $meth.name;
sub build_meth_info(@methods, @meth_names, %meth_info_to_use, @meth_names_to_use, %meth_providers_to_use) {
my $meth_iterator := nqp::iterator(@methods);
for @meth_names -> $name {
my $meth := nqp::shift($meth_iterator);
my @meth_list;
my @meth_providers;
if nqp::existskey(%meth_info_to_use, $name) {
@@ -44,9 +45,20 @@ my class RoleToRoleApplier {
}
}
}
build_meth_info(nqp::hllize($_.HOW.methods($_, :local(1))), %meth_info, @meth_names, %meth_providers);
build_meth_info(nqp::hllize($_.HOW.private_methods($_)), %priv_meth_info, @priv_meth_names, %priv_meth_providers)
if nqp::can($_.HOW, 'private_methods');
build_meth_info(
nqp::hllize($_.HOW.method_order($_)),
nqp::hllize($_.HOW.method_names($_)),
%meth_info,
@meth_names,
%meth_providers
);
build_meth_info(
nqp::hllize($_.HOW.private_methods($_)),
nqp::hllize($_.HOW.private_method_names($_)),
%priv_meth_info,
@priv_meth_names,
%priv_meth_providers
) if nqp::can($_.HOW, 'private_method_table');
}

# Also need methods of target.
@@ -1 +1 @@
2019.03-81-g21151f4b4
2019.03-82-g0a1d21573

0 comments on commit e274f4a

Please sign in to comment.
You can’t perform that action at this time.