Skip to content
Permalink
Browse files

Add methods from role to role in predictable order

  • Loading branch information...
niner committed May 1, 2019
1 parent 5b8b9c0 commit 5dcc687e239d6ecc4f4ca16c77cee370a83b8d46
Showing with 25 additions and 19 deletions.
  1. +6 −0 src/Perl6/Metamodel/PrivateMethodContainer.nqp
  2. +19 −19 src/Perl6/Metamodel/RoleToRoleApplier.nqp
@@ -1,5 +1,6 @@
role Perl6::Metamodel::PrivateMethodContainer {
has %!private_methods;
has @!private_methods;

# Adds a private method.
method add_private_method($obj, $name, $code) {
@@ -8,13 +9,18 @@ role Perl6::Metamodel::PrivateMethodContainer {
self.name($obj));
}
%!private_methods{$name} := $code;
nqp::push(@!private_methods, $code);
}

# Gets the table of private methods.
method private_method_table($obj) {
%!private_methods
}

method private_methods($obj) {
@!private_methods
}

# Locates a private method, and hands back null if it doesn't exist.
method find_private_method($obj, $name) {
nqp::existskey(%!private_methods, $name) ??
@@ -8,15 +8,16 @@ my class RoleToRoleApplier {
# Aggregate all of the methods sharing names, eliminating
# any duplicates (a method can't collide with itself).
my %meth_info;
my @meth_names;
my %meth_providers;
my %priv_meth_info;
my @priv_meth_names;
my %priv_meth_providers;
for @roles {
my $role := $_;
sub build_meth_info(%methods, %meth_info_to_use, %meth_providers_to_use) {
for %methods {
my $name := $_.key;
my $meth := $_.value;
sub build_meth_info(@methods, %meth_info_to_use, @meth_names_to_use, %meth_providers_to_use) {
for @methods -> $meth {
my $name := $meth.name;
my @meth_list;
my @meth_providers;
if nqp::existskey(%meth_info_to_use, $name) {
@@ -25,6 +26,7 @@ my class RoleToRoleApplier {
}
else {
%meth_info_to_use{$name} := @meth_list;
nqp::push(@meth_names_to_use, $name);
%meth_providers_to_use{$name} := @meth_providers;
}
my $found := 0;
@@ -42,19 +44,16 @@ my class RoleToRoleApplier {
}
}
}
build_meth_info(nqp::hllize($_.HOW.method_table($_)), %meth_info, %meth_providers);
build_meth_info(nqp::hllize($_.HOW.submethod_table($_)), %meth_info, %meth_providers)
if nqp::can($_.HOW, 'submethod_table');
build_meth_info(nqp::hllize($_.HOW.private_method_table($_)), %priv_meth_info, %priv_meth_providers)
if nqp::can($_.HOW, 'private_method_table');
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');
}

# Also need methods of target.
my %target_meth_info := nqp::hllize($target.HOW.method_table($target));

# Process method list.
for %meth_info {
my $name := $_.key;
for @meth_names -> $name {
my @add_meths := %meth_info{$name};

# Do we already have a method of this name? If so, ignore all of the
@@ -95,8 +94,7 @@ my class RoleToRoleApplier {
# Process private method list.
if nqp::can($target.HOW, 'private_method_table') {
my %target_priv_meth_info := nqp::hllize($target.HOW.private_method_table($target));
for %priv_meth_info {
my $name := $_.key;
for @priv_meth_names -> $name {
my @add_meths := %priv_meth_info{$name};
unless nqp::existskey(%target_priv_meth_info, $name) {
if +@add_meths == 1 {
@@ -134,7 +132,9 @@ my class RoleToRoleApplier {

# Compose multi-methods; need to pay attention to the signatures.
my %multis_by_name;
my @multi_names;
my %multis_required_by_name;
my @multis_required_names;
for @roles -> $role {
my $how := $role.HOW;
if nqp::can($how, 'multi_methods_to_incorporate') {
@@ -147,6 +147,7 @@ my class RoleToRoleApplier {
%multis_required_by_name{$name} := []
unless %multis_required_by_name{$name};
nqp::push(%multis_required_by_name{$name}, $to_add);
nqp::push(@multis_required_names, $name);
}
else {
if %multis_by_name{$name} -> @existing {
@@ -162,16 +163,16 @@ my class RoleToRoleApplier {
}
else {
%multis_by_name{$name} := [[$role, $to_add],];
nqp::push(@multi_names, $name);
}
}
}
}
}

# Look for conflicts, and compose non-conflicting.
for %multis_by_name {
my $name := $_.key;
my @cands := $_.value;
for @multi_names -> $name {
my @cands := %multis_by_name{$name};
my @collisions;
for @cands -> $c1 {
my @collides;
@@ -197,9 +198,8 @@ my class RoleToRoleApplier {
# Pass on any unsatisfied requirements (note that we check for the
# requirements being met when applying the summation of roles to a
# class, so we can avoid duplicating that logic here.)
for %multis_required_by_name {
my $name := $_.key;
for $_.value {
for @multis_required_names -> $name {
for %multis_required_by_name{$name} {
$target.HOW.add_multi_method($target, $name, $_);
}
}

0 comments on commit 5dcc687

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