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

Reply via email to