Skip to content

Commit

Permalink
Give NQP support for mixins.
Browse files Browse the repository at this point in the history
  • Loading branch information
jnthn committed Jul 26, 2012
1 parent 54df5ca commit f0c85fd
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/how/NQPClassHOW.pm
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,47 @@ knowhow NQPClassHOW {
(%caches{nqp::where(self)}{$key} := $value_generator())
}


##
## Mix-ins
##
has @!mixin_cache;
method mixin($obj, $role) {
# See if we mixed in before.
my $found := 0;
my $new_type;
for @!mixin_cache -> $c_role, $c_type {
if $c_role =:= $role {
$new_type := $c_type;
$found := 1;
last;
}
}

# Create and cache mixin-type if needed.
unless $found {
# Work out a type name for the post-mixed-in role.
my $new_name := self.name($obj) ~ '+{' ~ $role.HOW.name($role) ~ '}';

# Create new type, derive it from ourself and then add
# all the roles we're mixing it.
$new_type := self.new_type(:name($new_name), :repr($obj.REPR));
$new_type.HOW.add_parent($new_type, $obj.WHAT);
$new_type.HOW.add_role($new_type, $role);
$new_type.HOW.compose($new_type);

# Store the type.
@!mixin_cache[+@!mixin_cache] := $role;
@!mixin_cache[+@!mixin_cache] := $new_type;
}

# If the original object was concrete, change its type by calling a
# low level op. Otherwise, we just return the new type object
nqp::isconcrete($obj) ??
pir::repr_change_type__0PP($obj, $new_type) !!
$new_type
}

##
## Tracing
##
Expand Down
29 changes: 29 additions & 0 deletions t/nqp/61-mixin.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
plan(6);

role TheRole {
has $!a;
method role_meth() { "called role method" }
method get_attr() { $!a }
method set_attr($v) { $!a := $v }
method override_me() { "role method" }
}

class Example {
method override_me() { "class method" }
}

my $obj := Example.new();
my $obj_m := Example.new();

ok($obj.override_me() eq "class method", "sanity (1)");
ok($obj_m.override_me() eq "class method", "sanity (2)");

$obj_m.HOW.mixin($obj_m, TheRole);

ok($obj_m.role_meth() eq "called role method", "role method mxied in");

$obj_m.set_attr("stout");
ok($obj_m.get_attr() eq "stout", "attributes from role work properly");

ok($obj_m.override_me() eq "role method", "mixed in method overrides original one");
ok($obj.override_me() eq "class method", "mixing in is per object");

0 comments on commit f0c85fd

Please sign in to comment.