Skip to content

Commit

Permalink
Add methods from role to role in predictable order
Browse files Browse the repository at this point in the history
  • Loading branch information
niner committed May 1, 2019
1 parent 5b8b9c0 commit 5dcc687
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 19 deletions.
6 changes: 6 additions & 0 deletions src/Perl6/Metamodel/PrivateMethodContainer.nqp
@@ -1,5 +1,6 @@
role Perl6::Metamodel::PrivateMethodContainer { role Perl6::Metamodel::PrivateMethodContainer {
has %!private_methods; has %!private_methods;
has @!private_methods;


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


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


method private_methods($obj) {
@!private_methods
}

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


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


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


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


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


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

0 comments on commit 5dcc687

Please sign in to comment.