Author: jonathan
Date: Mon Apr 2 16:06:30 2007
New Revision: 17949
Modified:
trunk/src/pmc/class.pmc
trunk/t/oo/composition.t
Log:
[PDD15]: Implement resolve for role conflict resolution, plus a test.
Modified: trunk/src/pmc/class.pmc
==============================================================================
--- trunk/src/pmc/class.pmc (original)
+++ trunk/src/pmc/class.pmc Mon Apr 2 16:06:30 2007
@@ -37,6 +37,8 @@
PMC *attrib_metadata; /* Hash of attributes in this class to hashes of
metadata. */
PMC *attrib_index; /* Lookup table for attributes in this and parents.
*/
PMC *attrib_cache; /* Cache of visible attrib names to indexes. */
+ PMC *resolve; /* List of method names the class provides to resolve
+ * conflicts with methods from roles. */
} Parrot_Class;
@@ -149,6 +151,7 @@
class->attrib_metadata = pmc_new(interp, enum_class_Hash);
class->attrib_index = PMCNULL;
class->attrib_cache = PMCNULL;
+ class->resolve = pmc_new(interp, enum_class_ResizablePMCArray);
/* We put ourself on the all parents list. */
VTABLE_push_pmc(interp, class->all_parents, SELF);
@@ -212,6 +215,8 @@
pobject_lives(interp, (PObj*)class->attrib_index);
if (class->attrib_cache)
pobject_lives(interp, (PObj*)class->attrib_cache);
+ if (class->resolve)
+ pobject_lives(interp, (PObj*)class->resolve);
}
@@ -338,6 +343,29 @@
/*
+=item C<void resolve()>
+
+Sets the list of method names that the class provides to resolve conflicts in
+methods from roles. When called with no parameter, returns the list.
+
+=cut
+
+*/
+ PCCMETHOD void resolve(PMC *resolve_list :optional, int got_list
:opt_flag) {
+ Parrot_Class *class = PARROT_CLASS(SELF);
+ PMC *ret_list = NULL;
+
+ if (got_list) {
+ /* Store list. */
+ class->resolve = resolve_list;
+ }
+
+ ret_list = class->resolve;
+ PCCRETURN(PMC *ret_list);
+ }
+
+/*
+
=item C<void new(PMC *args :slurpy :named)>
Creates an instance of the object. Initializes any attributes specified in the
@@ -353,6 +381,19 @@
/* If we've not been instantiated before... */
if (!class->instantiated) {
+ /* Check that we have all methods listed in resolve list. */
+ int resolve_count = VTABLE_elements(interp, class->resolve);
+ int i;
+ for (i = 0; i < resolve_count; i++) {
+ STRING *check_meth = VTABLE_get_string_keyed_int(interp,
+ class->resolve, i);
+ if (!VTABLE_exists_keyed_str(interp, class->methods,
check_meth)) {
+ real_exception(interp, NULL, METH_NOT_FOUND,
+ "The method '%S' was named in the resolve list, but
not supplied",
+ check_meth);
+ }
+ }
+
/* Build full parents list.
* XXX Need pluggable MRO, for now always do C3. */
class->all_parents = Parrot_ComputeMRO_C3(interp, SELF);
@@ -484,6 +525,27 @@
PMC* exclude :optional :named["exclude"], int got_exclude
:opt_flag,
PMC* alias :optional :named["alias"], int got_alias :opt_flag)
{
Parrot_Class *class = PARROT_CLASS(SELF);
+
+ /* Add everything on the resolve list to the exclude list; if we have
+ * no exclude list, pass along the resolve list in its place if it has
+ * any methods listed in it. */
+ if (!got_exclude) {
+ if (VTABLE_elements(interp, class->resolve) != 0) {
+ exclude = class->resolve;
+ got_exclude = 1;
+ }
+ }
+ else {
+ int resolve_count = VTABLE_elements(interp, class->resolve);
+ int i;
+ for (i = 0; i < resolve_count; i++) {
+ STRING *meth_name = VTABLE_get_string_keyed_int(interp,
+ class->resolve, i);
+ VTABLE_push_string(interp, exclude, meth_name);
+ }
+ }
+
+ /* Do the composition. */
Parrot_ComposeRole(interp, role, exclude, got_exclude, alias,
got_alias,
class->methods, class->roles);
}
Modified: trunk/t/oo/composition.t
==============================================================================
--- trunk/t/oo/composition.t (original)
+++ trunk/t/oo/composition.t Mon Apr 2 16:06:30 2007
@@ -6,7 +6,7 @@
use warnings;
use lib qw( . lib ../lib ../../lib );
use Test::More;
-use Parrot::Test tests => 9;
+use Parrot::Test tests => 10;
=head1 NAME
@@ -294,6 +294,62 @@
ok 5 - called method from role that was aliased
OUT
+pir_output_is( <<'CODE', <<'OUT', 'conflict resolution by resolve' );
+.sub 'test' :main
+ $P0 = new Role
+ $P1 = new Class
+
+ $P3 = new ResizableStringArray
+ push $P3, "badger"
+ $P1.resolve($P3)
+ print "ok 1 - set resolve list\n"
+
+ $P4 = $P1.resolve()
+ $S0 = $P4[0]
+ if $S0 == "badger" goto ok_2
+ print "not "
+ok_2:
+ print "ok 2 - got resolve list and it matched\n"
+
+ $P2 = find_global "badger"
+ $P1.'add_method'("badger", $P2)
+ print "ok 3 - class has a method\n"
+
+ $P2 = find_global "badger2"
+ $P0.'add_method'("badger", $P2)
+ $P2 = find_global "snake"
+ $P0.'add_method'("snake", $P2)
+ $P1.'add_role'($P0)
+ print "ok 4 - composition worked due to resolve\n"
+
+ $P2 = $P1.'new'()
+ $P2.'badger'()
+ print "ok 5 - called method from class\n"
+
+ $P2.'snake'()
+ print "ok 6 - called method from role that wasn't resolved\n"
+.end
+
+.sub badger :method
+ print "Badger!\n"
+.end
+.sub badger2 :method
+ print "Oops, wrong badger.\n"
+.end
+.sub snake :method
+ print "Snake!\n"
+.end
+CODE
+ok 1 - set resolve list
+ok 2 - got resolve list and it matched
+ok 3 - class has a method
+ok 4 - composition worked due to resolve
+Badger!
+ok 5 - called method from class
+Snake!
+ok 6 - called method from role that wasn't resolved
+OUT
+
pir_output_is( <<'CODE', <<'OUT', 'role that does a role' );
.sub 'test' :main
.local pmc PHB, Manage, FirePeople