[t] Refactor some namespace pmc tests to use throws_like
[parrot.git] / src / oo.c
blob107062412f2717db18313784b9d45af963dc5750
1 /*
2 Copyright (C) 2007-2009, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 oo.c - Class and object
9 =head1 DESCRIPTION
11 Handles class and object manipulation.
13 =head2 Functions
15 =over 4
17 =cut
21 #define PARROT_IN_OO_C
22 #define PARROT_IN_OBJECTS_C /* To get the vtable.h imports we want. */
23 #include "parrot/parrot.h"
24 #include "parrot/oo_private.h"
25 #include "pmc/pmc_class.h"
26 #include "pmc/pmc_object.h"
28 #include "oo.str"
30 /* HEADERIZER HFILE: include/parrot/oo.h */
32 /* HEADERIZER BEGIN: static */
33 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
35 PARROT_WARN_UNUSED_RESULT
36 PARROT_CAN_RETURN_NULL
37 static PMC* C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
38 __attribute__nonnull__(1)
39 __attribute__nonnull__(2);
41 static void debug_trace_find_meth(PARROT_INTERP,
42 ARGIN(const PMC *_class),
43 ARGIN(const STRING *name),
44 ARGIN_NULLOK(const PMC *sub))
45 __attribute__nonnull__(1)
46 __attribute__nonnull__(2)
47 __attribute__nonnull__(3);
49 static INTVAL fail_if_type_exists(PARROT_INTERP, ARGIN(PMC *name))
50 __attribute__nonnull__(1)
51 __attribute__nonnull__(2);
53 PARROT_WARN_UNUSED_RESULT
54 PARROT_CAN_RETURN_NULL
55 static PMC * find_method_direct_1(PARROT_INTERP,
56 ARGIN(PMC *_class),
57 ARGIN(STRING *method_name))
58 __attribute__nonnull__(1)
59 __attribute__nonnull__(2)
60 __attribute__nonnull__(3);
62 PARROT_INLINE
63 PARROT_CANNOT_RETURN_NULL
64 PARROT_WARN_UNUSED_RESULT
65 static PMC * get_pmc_proxy(PARROT_INTERP, INTVAL type)
66 __attribute__nonnull__(1);
68 static void invalidate_all_caches(PARROT_INTERP)
69 __attribute__nonnull__(1);
71 static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)
72 __attribute__nonnull__(1);
74 #define ASSERT_ARGS_C3_merge __attribute__unused__ int _ASSERT_ARGS_CHECK = \
75 PARROT_ASSERT_ARG(interp) \
76 || PARROT_ASSERT_ARG(merge_list)
77 #define ASSERT_ARGS_debug_trace_find_meth __attribute__unused__ int _ASSERT_ARGS_CHECK = \
78 PARROT_ASSERT_ARG(interp) \
79 || PARROT_ASSERT_ARG(_class) \
80 || PARROT_ASSERT_ARG(name)
81 #define ASSERT_ARGS_fail_if_type_exists __attribute__unused__ int _ASSERT_ARGS_CHECK = \
82 PARROT_ASSERT_ARG(interp) \
83 || PARROT_ASSERT_ARG(name)
84 #define ASSERT_ARGS_find_method_direct_1 __attribute__unused__ int _ASSERT_ARGS_CHECK = \
85 PARROT_ASSERT_ARG(interp) \
86 || PARROT_ASSERT_ARG(_class) \
87 || PARROT_ASSERT_ARG(method_name)
88 #define ASSERT_ARGS_get_pmc_proxy __attribute__unused__ int _ASSERT_ARGS_CHECK = \
89 PARROT_ASSERT_ARG(interp)
90 #define ASSERT_ARGS_invalidate_all_caches __attribute__unused__ int _ASSERT_ARGS_CHECK = \
91 PARROT_ASSERT_ARG(interp)
92 #define ASSERT_ARGS_invalidate_type_caches __attribute__unused__ int _ASSERT_ARGS_CHECK = \
93 PARROT_ASSERT_ARG(interp)
94 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
95 /* HEADERIZER END: static */
99 =item C<void Parrot_oo_extract_methods_from_namespace(PARROT_INTERP, PMC *self,
100 PMC *ns)>
102 Extract methods and vtable overrides from the given namespace and insert them
103 into the class.
105 =cut
109 void
110 Parrot_oo_extract_methods_from_namespace(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *ns))
112 ASSERT_ARGS(Parrot_oo_extract_methods_from_namespace)
113 PMC *methods, *vtable_overrides;
115 /* Pull in methods from the namespace, if any. */
116 if (PMC_IS_NULL(ns))
117 return;
119 /* Import any methods. */
120 Parrot_PCCINVOKE(interp, ns, CONST_STRING(interp, "get_associated_methods"), "->P", &methods);
122 if (!PMC_IS_NULL(methods)) {
123 PMC * const iter = VTABLE_get_iter(interp, methods);
125 while (VTABLE_get_bool(interp, iter)) {
126 STRING * const meth_name = VTABLE_shift_string(interp, iter);
127 PMC * const meth_sub = VTABLE_get_pmc_keyed_str(interp, methods,
128 meth_name);
129 VTABLE_add_method(interp, self, meth_name, meth_sub);
133 /* Import any vtable methods. */
134 Parrot_PCCINVOKE(interp, ns, CONST_STRING(interp, "get_associated_vtable_methods"), "->P", &vtable_overrides);
136 if (!PMC_IS_NULL(vtable_overrides)) {
137 PMC * const iter = VTABLE_get_iter(interp, vtable_overrides);
138 while (VTABLE_get_bool(interp, iter)) {
139 STRING * const vtable_index_str = VTABLE_shift_string(interp, iter);
140 PMC * const vtable_sub = VTABLE_get_pmc_keyed_str(interp,
141 vtable_overrides, vtable_index_str);
143 /* Look up the name of the vtable function from the index. */
144 const INTVAL vtable_index = Parrot_str_to_int(interp, vtable_index_str);
145 const char * const meth_c = Parrot_vtable_slot_names[vtable_index];
146 STRING * const vtable_name = Parrot_str_new(interp, meth_c, 0);
147 VTABLE_add_vtable_override(interp, self, vtable_name, vtable_sub);
155 =item C<PMC * Parrot_oo_get_namespace(PARROT_INTERP, const PMC *classobj)>
157 Lookup a namespace object from a class PMC.
159 =cut
163 PARROT_CAN_RETURN_NULL
164 PARROT_WARN_UNUSED_RESULT
165 PMC *
166 Parrot_oo_get_namespace(SHIM_INTERP, ARGIN(const PMC *classobj))
168 ASSERT_ARGS(Parrot_oo_get_namespace)
169 Parrot_Class_attributes * const _class = PARROT_CLASS(classobj);
170 PMC * const _namespace = _class->_namespace;
172 if (PMC_IS_NULL(_namespace))
173 return PMCNULL;
175 return _namespace;
181 =item C<PMC * Parrot_oo_get_class(PARROT_INTERP, PMC *key)>
183 Lookup a class object from a namespace, string, or key PMC.
185 =cut
189 PARROT_EXPORT
190 PARROT_CAN_RETURN_NULL
191 PARROT_WARN_UNUSED_RESULT
192 PMC *
193 Parrot_oo_get_class(PARROT_INTERP, ARGIN(PMC *key))
195 ASSERT_ARGS(Parrot_oo_get_class)
196 PMC *classobj = PMCNULL;
198 if (PObj_is_class_TEST(key))
199 classobj = key;
200 else {
201 /* Fast select of behavior based on type of the lookup key */
202 switch (key->vtable->base_type) {
203 case enum_class_NameSpace:
204 classobj = VTABLE_get_class(interp, key);
205 break;
206 case enum_class_String:
207 case enum_class_Key:
208 case enum_class_ResizableStringArray:
210 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp,
211 interp->HLL_namespace,
212 Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
213 PMC * const ns = Parrot_get_namespace_keyed(interp,
214 hll_ns, key);
216 if (!PMC_IS_NULL(ns))
217 classobj = VTABLE_get_class(interp, ns);
219 default:
220 break;
224 /* If the PMCProxy doesn't exist yet for the given key, we look up the
225 type ID here and create a new one */
226 if (PMC_IS_NULL(classobj)) {
227 INTVAL type;
228 const INTVAL base_type = key->vtable->base_type;
230 /* This is a hack! All PMCs should be able to be handled through
231 a single codepath, and all of them should be able to avoid
232 stringification because it's so imprecise. */
233 if (base_type == enum_class_Key
234 || base_type == enum_class_ResizableStringArray
235 || base_type == enum_class_String)
236 type = pmc_type_p(interp, key);
237 else
238 type = pmc_type(interp, VTABLE_get_string(interp, key));
240 classobj = get_pmc_proxy(interp, type);
243 return classobj;
248 =item C<PMC * Parrot_oo_clone_object(PARROT_INTERP, PMC *pmc, PMC *class_, PMC
249 *dest)>
251 Clone an Object PMC. If an existing PMC C<dest> is provided, reuse that
252 PMC to store copies of the data. Otherwise, create a new PMC and populate
253 that with the data.
255 =cut
259 PARROT_CANNOT_RETURN_NULL
260 PMC *
261 Parrot_oo_clone_object(PARROT_INTERP, ARGIN(PMC *pmc),
262 ARGMOD_NULLOK(PMC *class_), ARGMOD_NULLOK(PMC *dest))
264 ASSERT_ARGS(Parrot_oo_clone_object)
265 Parrot_Object_attributes *obj;
266 Parrot_Object_attributes *cloned_guts;
267 Parrot_Class_attributes *_class;
268 PMC *cloned;
269 INTVAL num_classes;
270 INTVAL i, num_attrs;
272 if (!PMC_IS_NULL(dest)) {
273 PARROT_ASSERT(!PMC_IS_NULL(class_));
274 PARROT_ASSERT(class_->vtable->base_type == enum_class_Class);
275 obj = PARROT_OBJECT(pmc);
276 cloned = dest;
278 else {
279 obj = PARROT_OBJECT(pmc);
280 cloned = pmc_new_noinit(interp, enum_class_Object);
283 _class = PARROT_CLASS(obj->_class);
284 PARROT_ASSERT(_class);
285 num_classes = VTABLE_elements(interp, _class->all_parents);
287 /* Set custom GC mark and destroy on the object. */
288 PObj_custom_mark_SET(cloned);
289 PObj_custom_destroy_SET(cloned);
291 /* Flag that it is an object */
292 PObj_is_object_SET(cloned);
294 /* Now clone attributes list.class. */
295 cloned_guts = (Parrot_Object_attributes *) PMC_data(cloned);
296 cloned_guts->_class = obj->_class;
297 cloned_guts->attrib_store = NULL;
298 cloned_guts->attrib_store = VTABLE_clone(interp, obj->attrib_store);
299 num_attrs = VTABLE_elements(interp, cloned_guts->attrib_store);
300 for (i = 0; i < num_attrs; i++) {
301 PMC * const to_clone = VTABLE_get_pmc_keyed_int(interp, cloned_guts->attrib_store, i);
302 if (!PMC_IS_NULL(to_clone)) {
303 VTABLE_set_pmc_keyed_int(interp, cloned_guts->attrib_store, i,
304 VTABLE_clone(interp, to_clone));
308 /* Some of the attributes may have been the PMCs providing storage for any
309 * PMCs we inherited from; also need to clone those. */
310 if (CLASS_has_alien_parents_TEST(obj->_class)) {
311 /* Locate any PMC parents. */
312 for (i = 0; i < num_classes; i++) {
313 PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp, _class->all_parents, i);
314 if (cur_class->vtable->base_type == enum_class_PMCProxy) {
315 /* Clone this PMC too. */
316 STRING * const proxy = CONST_STRING(interp, "proxy");
317 VTABLE_set_attr_keyed(interp, cloned, cur_class, proxy,
318 VTABLE_clone(interp,
319 VTABLE_get_attr_keyed(interp, cloned, cur_class, proxy)));
324 /* And we have ourselves a clone. */
325 return cloned;
330 =item C<void * Parrot_oo_new_object_attrs(PARROT_INTERP, PMC * class_)>
332 Create a new C<Parrot_Object_attributes> structure to hold data for an Object
333 PMC. We need this for places which create a new Object without instantiating it
334 through its associated class, such as in C<Parrot_oo_clone_object>.
336 =cut
340 PARROT_CANNOT_RETURN_NULL
341 void *
342 Parrot_oo_new_object_attrs(PARROT_INTERP, ARGIN(PMC * class_))
344 ASSERT_ARGS(Parrot_oo_new_object_attrs)
345 Parrot_Object_attributes * const obj_guts =
346 mem_allocate_typed(Parrot_Object_attributes);
347 obj_guts->_class = class_;
348 obj_guts->attrib_store = pmc_new(interp, enum_class_ResizablePMCArray);
349 return (void *)obj_guts;
354 =item C<static PMC * get_pmc_proxy(PARROT_INTERP, INTVAL type)>
356 Get the PMC proxy for a PMC with the given type, creating it if does not exist.
357 If type is not a valid type, return PMCNULL. This code assumes that
358 all PMCProxy objects live in the 'parrot' HLL namespace -- if/when
359 we allow PMC types to exist in other HLL namespaces, this code will
360 need to be updated.
362 For internal use only.
364 =cut
368 PARROT_INLINE
369 PARROT_CANNOT_RETURN_NULL
370 PARROT_WARN_UNUSED_RESULT
371 static PMC *
372 get_pmc_proxy(PARROT_INTERP, INTVAL type)
374 ASSERT_ARGS(get_pmc_proxy)
375 PMC * type_class;
377 /* Check if not a PMC or invalid type number */
378 if (type > interp->n_vtable_max || type <= 0)
379 return PMCNULL;
381 type_class = interp->vtables[type]->pmc_class;
382 if (type != enum_class_Class
383 && type_class->vtable->base_type == enum_class_Class) {
384 return type_class;
386 else {
387 PMC * const parrot_hll = Parrot_get_namespace_keyed_str(interp, interp->root_namespace, CONST_STRING(interp, "parrot"));
388 PMC * const pmc_ns =
389 Parrot_make_namespace_keyed_str(interp, parrot_hll,
390 interp->vtables[type]->whoami);
391 PMC * proxy = VTABLE_get_class(interp, pmc_ns);
393 /* Create proxy if not found */
394 if (PMC_IS_NULL(proxy)) {
395 PMC * const type_num = pmc_new(interp, enum_class_Integer);
396 VTABLE_set_integer_native(interp, type_num, type);
397 proxy = pmc_new_init(interp, enum_class_PMCProxy, type_num);
398 Parrot_PCCINVOKE(interp, pmc_ns, CONST_STRING(interp, "set_class"), "P->", proxy);
400 return proxy;
406 =item C<PMC * Parrot_oo_get_class_str(PARROT_INTERP, STRING *name)>
408 Lookup a class object from a builtin string.
410 =cut
414 PARROT_EXPORT
415 PARROT_CAN_RETURN_NULL
416 PARROT_WARN_UNUSED_RESULT
417 PMC *
418 Parrot_oo_get_class_str(PARROT_INTERP, ARGIN(STRING *name))
420 ASSERT_ARGS(Parrot_oo_get_class_str)
422 /* First check in current HLL namespace */
423 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp, interp->HLL_namespace,
424 Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
425 PMC * const ns = Parrot_get_namespace_keyed_str(interp, hll_ns, name);
426 PMC * const _class = PMC_IS_NULL(ns)
427 ? PMCNULL : VTABLE_get_class(interp, ns);
429 /* If not found, check for a PMC */
430 if (PMC_IS_NULL(_class))
431 return get_pmc_proxy(interp, pmc_type(interp, name));
432 else
433 return _class;
439 =item C<PMC * Parrot_oo_newclass_from_str(PARROT_INTERP, STRING *name)>
441 Create a new class object from a string name.
443 =cut
447 PARROT_CAN_RETURN_NULL
448 PARROT_WARN_UNUSED_RESULT
449 PMC *
450 Parrot_oo_newclass_from_str(PARROT_INTERP, ARGIN(STRING *name))
452 ASSERT_ARGS(Parrot_oo_newclass_from_str)
453 PMC * const namearg = pmc_new(interp, enum_class_String);
454 PMC * const namehash = pmc_new(interp, enum_class_Hash);
455 PMC *classobj;
457 VTABLE_set_string_native(interp, namearg, name);
458 VTABLE_set_pmc_keyed_str(interp, namehash, CONST_STRING(interp, "name"), namearg);
460 classobj = pmc_new_init(interp, enum_class_Class, namehash);
462 PARROT_ASSERT(classobj);
463 return classobj;
469 =item C<PMC * Parrot_oo_find_vtable_override_for_class(PARROT_INTERP, PMC
470 *classobj, STRING *name)>
472 Lookup a vtable override in a specific class object.
474 =cut
478 PARROT_EXPORT
479 PARROT_CAN_RETURN_NULL
480 PARROT_WARN_UNUSED_RESULT
481 PMC *
482 Parrot_oo_find_vtable_override_for_class(PARROT_INTERP,
483 ARGIN(PMC *classobj), ARGIN(STRING *name))
485 ASSERT_ARGS(Parrot_oo_find_vtable_override_for_class)
486 Parrot_Class_attributes *class_info;
487 PARROT_ASSERT(PObj_is_class_TEST(classobj));
489 class_info = PARROT_CLASS(classobj);
490 return VTABLE_get_pmc_keyed_str(interp, class_info->vtable_overrides, name);
496 =item C<PMC * Parrot_oo_find_vtable_override(PARROT_INTERP, PMC *classobj,
497 STRING *name)>
499 Lookup a vtable override in a class, including any vtable overrides inherited
500 from parents.
502 =cut
506 PARROT_EXPORT
507 PARROT_CAN_RETURN_NULL
508 PARROT_WARN_UNUSED_RESULT
509 PMC *
510 Parrot_oo_find_vtable_override(PARROT_INTERP,
511 ARGIN(PMC *classobj), ARGIN(STRING *name))
513 ASSERT_ARGS(Parrot_oo_find_vtable_override)
514 Parrot_Class_attributes * const _class = PARROT_CLASS(classobj);
515 PMC *result =
516 VTABLE_get_pmc_keyed_str(interp, _class->parent_overrides, name);
518 if (PMC_IS_NULL(result)) {
519 /* Walk and search for the vtable method. */
520 const INTVAL num_classes = VTABLE_elements(interp, _class->all_parents);
521 INTVAL i;
523 for (i = 0; i < num_classes; i++) {
524 /* Get the class. */
525 PMC * const cur_class =
526 VTABLE_get_pmc_keyed_int(interp, _class->all_parents, i);
528 result = Parrot_oo_find_vtable_override_for_class(interp,
529 cur_class, name);
531 if (!PMC_IS_NULL(result))
532 break;
534 if (PMC_IS_NULL(result))
535 result = pmc_new(interp, enum_class_Undef);
536 VTABLE_set_pmc_keyed_str(interp, _class->parent_overrides, name, result);
538 if (result->vtable->base_type == enum_class_Undef)
539 result = PMCNULL;
540 return result;
546 =item C<INTVAL Parrot_get_vtable_index(PARROT_INTERP, const STRING *name)>
548 Return index if C<name> is a valid vtable slot name.
550 =cut
554 PARROT_EXPORT
555 INTVAL
556 Parrot_get_vtable_index(PARROT_INTERP, ARGIN(const STRING *name))
558 ASSERT_ARGS(Parrot_get_vtable_index)
559 char * const name_c = Parrot_str_to_cstring(interp, name);
561 /* some of the first "slots" don't have names. skip 'em. */
562 INTVAL low = PARROT_VTABLE_LOW;
563 INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW;
565 while (low < high) {
566 const INTVAL mid = (low + high) / 2;
567 const char * const meth_c = Parrot_vtable_slot_names[mid];
569 const INTVAL cmp = strcmp(name_c, meth_c);
571 if (cmp == 0) {
572 Parrot_str_free_cstring(name_c);
573 return mid;
575 else if (cmp > 0)
576 low = mid + 1;
577 else
578 high = mid;
581 Parrot_str_free_cstring(name_c);
583 return -1;
589 =item C<const char * Parrot_get_vtable_name(PARROT_INTERP, INTVAL idx)>
591 Return the method name at the specified index in the vtable slot array.
592 Use this function when you cannot access Parrot_vtable_slot_names directly.
594 =cut
598 PARROT_EXPORT
599 PARROT_PURE_FUNCTION
600 PARROT_CAN_RETURN_NULL
601 const char *
602 Parrot_get_vtable_name(SHIM_INTERP, INTVAL idx)
604 ASSERT_ARGS(Parrot_get_vtable_name)
606 const INTVAL low = PARROT_VTABLE_LOW;
607 const INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW;
609 PARROT_ASSERT(idx > 0);
611 if (idx < low || idx > high) {
612 return NULL;
615 return Parrot_vtable_slot_names[idx];
621 =item C<static INTVAL fail_if_type_exists(PARROT_INTERP, PMC *name)>
623 This function throws an exception if a PMC or class with the same name *
624 already exists in the global type registry. The global type registry
625 will go away eventually, but this allows the new object metamodel to
626 interact with the old one until it does.
628 =cut
632 static INTVAL
633 fail_if_type_exists(PARROT_INTERP, ARGIN(PMC *name))
635 ASSERT_ARGS(fail_if_type_exists)
636 PMC * const value = (PMC *)VTABLE_get_pointer_keyed(interp, interp->class_hash, name);
638 if (PMC_IS_NULL(value))
639 return 0;
641 switch (VTABLE_type(interp, value)) {
642 case enum_class_NameSpace:
643 return 0;
644 break;
645 case enum_class_Integer:
647 const INTVAL type = VTABLE_get_integer(interp, value);
648 if (type < enum_type_undef) {
649 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
650 "native type with name '%s' already exists - "
651 "can't register Class", data_types[type].name);
653 return type;
655 break;
656 default:
657 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERP_ERROR,
658 "Unrecognized class name PMC type");
659 break;
666 =item C<INTVAL Parrot_oo_register_type(PARROT_INTERP, PMC *name, PMC
667 *_namespace)>
669 This function registers a type in the global registry, first checking if it
670 already exists. The global type registry will go away eventually, but this
671 allows the new object metamodel to interact with the old one until it does.
673 =cut
677 PARROT_WARN_UNUSED_RESULT
678 INTVAL
679 Parrot_oo_register_type(PARROT_INTERP, ARGIN(PMC *name), ARGIN(PMC *_namespace))
681 ASSERT_ARGS(Parrot_oo_register_type)
682 INTVAL type;
683 const INTVAL typeid_exists = fail_if_type_exists(interp, name);
685 PMC * const classobj = VTABLE_get_class(interp, _namespace);
686 if (!PMC_IS_NULL(classobj)) {
687 STRING * const classname = VTABLE_get_string(interp, _namespace);
688 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
689 "Class %Ss already registered!\n",
690 Parrot_str_escape(interp, classname));
693 /* Type doesn't exist, so go ahead and register it. Lock interpreter so
694 * pt_shared_fixup() can safely do a type lookup. */
695 LOCK_INTERPRETER(interp);
697 type = get_new_vtable_index(interp);
700 if (!typeid_exists) {
701 PMC * const classname_hash = interp->class_hash;
702 PMC * const item = pmc_new(interp, enum_class_Integer);
703 /* set entry in name->type hash */
704 VTABLE_set_integer_native(interp, item, type);
706 VTABLE_set_pmc_keyed(interp, classname_hash, name, item);
709 UNLOCK_INTERPRETER(interp);
711 return type;
716 =item C<void mark_object_cache(PARROT_INTERP)>
718 Marks all PMCs in the object method cache as live. This shouldn't strictly be
719 necessary, as they're likely all reachable from namespaces and classes, but
720 it's unlikely to hurt anything except mark phase performance.
722 =cut
726 #define TBL_SIZE_MASK 0x1ff /* x bits 2..10 */
727 #define TBL_SIZE (1 + TBL_SIZE_MASK)
729 void
730 mark_object_cache(PARROT_INTERP)
732 ASSERT_ARGS(mark_object_cache)
733 Caches * const mc = interp->caches;
734 UINTVAL type, entry;
736 if (!mc)
737 return;
739 for (type = 0; type < mc->mc_size; type++) {
740 if (!mc->idx[type])
741 continue;
743 for (entry = 0; entry < TBL_SIZE; ++entry) {
744 Meth_cache_entry *e = mc->idx[type][entry];
745 while (e) {
746 Parrot_gc_mark_PObj_alive(interp, (PObj *)e->pmc);
747 e = e->next;
756 =item C<void init_object_cache(PARROT_INTERP)>
758 Allocate memory for object cache.
760 =cut
764 void
765 init_object_cache(PARROT_INTERP)
767 ASSERT_ARGS(init_object_cache)
768 Caches * const mc = interp->caches = mem_allocate_zeroed_typed(Caches);
769 mc->idx = NULL;
775 =item C<void destroy_object_cache(PARROT_INTERP)>
777 Destroy the object cache. Loop over all caches and invalidate them. Then
778 free the caches back to the OS.
780 =cut
784 void
785 destroy_object_cache(PARROT_INTERP)
787 ASSERT_ARGS(destroy_object_cache)
788 UINTVAL i;
789 Caches * const mc = interp->caches;
791 /* mc->idx[type][bits] = e; */
792 for (i = 0; i < mc->mc_size; i++) {
793 if (mc->idx[i])
794 invalidate_type_caches(interp, i);
797 mem_sys_free(mc->idx);
798 mem_sys_free(mc);
804 =item C<static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)>
806 Invalidate the cache of the specified type. Free each entry and then free
807 the entire cache.
809 =cut
813 static void
814 invalidate_type_caches(PARROT_INTERP, UINTVAL type)
816 ASSERT_ARGS(invalidate_type_caches)
817 Caches * const mc = interp->caches;
818 INTVAL i;
820 if (!mc)
821 return;
823 /* is it a valid entry */
824 if (type >= mc->mc_size || !mc->idx[type])
825 return;
827 for (i = 0; i < TBL_SIZE; ++i) {
828 Meth_cache_entry *e = mc->idx[type][i];
829 while (e) {
830 Meth_cache_entry * const next = e->next;
831 mem_sys_free(e);
832 e = next;
836 mem_sys_free(mc->idx[type]);
837 mc->idx[type] = NULL;
843 =item C<static void invalidate_all_caches(PARROT_INTERP)>
845 Invalidate all caches by looping over each cache and calling
846 C<invalidate_type_caches> on them.
848 =cut
852 static void
853 invalidate_all_caches(PARROT_INTERP)
855 ASSERT_ARGS(invalidate_all_caches)
856 UINTVAL i;
857 for (i = 1; i < (UINTVAL)interp->n_vtable_max; ++i)
858 invalidate_type_caches(interp, i);
864 =item C<void Parrot_invalidate_method_cache(PARROT_INTERP, STRING *_class)>
866 Clear method cache for the given class. If class is NULL, caches for
867 all classes are invalidated.
869 =cut
873 PARROT_EXPORT
874 void
875 Parrot_invalidate_method_cache(PARROT_INTERP, ARGIN_NULLOK(STRING *_class))
877 ASSERT_ARGS(Parrot_invalidate_method_cache)
878 INTVAL type;
880 /* during interp creation and NCI registration the class_hash
881 * isn't yet up */
882 if (!interp->class_hash)
883 return;
885 if (interp->resume_flag & RESUME_INITIAL)
886 return;
888 if (!_class) {
889 invalidate_all_caches(interp);
890 return;
893 type = pmc_type(interp, _class);
895 if (type == 0)
896 invalidate_all_caches(interp);
897 else if (type > 0)
898 invalidate_type_caches(interp, (UINTVAL)type);
903 * quick'n'dirty method cache
904 * RT #45987: use a hash if method_name is not constant
905 * i.e. from obj.$Sreg(args)
906 * If this hash is implemented mark it during GC
911 =item C<PMC * Parrot_find_method_direct(PARROT_INTERP, PMC *_class, STRING
912 *method_name)>
914 Find a method PMC for a named method, given the class PMC, current
915 interpreter, and name of the method. Don't use a possible method cache.
917 =cut
921 PARROT_EXPORT
922 PARROT_CAN_RETURN_NULL
923 PARROT_WARN_UNUSED_RESULT
924 PMC *
925 Parrot_find_method_direct(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
927 ASSERT_ARGS(Parrot_find_method_direct)
928 PMC * const found = find_method_direct_1(interp, _class, method_name);
930 if (!PMC_IS_NULL(found))
931 return found;
934 if (Parrot_str_equal(interp, method_name, CONST_STRING(interp, "__get_string")))
935 return find_method_direct_1(interp, _class, CONST_STRING(interp, "__get_repr"));
937 return PMCNULL;
943 =item C<PMC * Parrot_find_method_with_cache(PARROT_INTERP, PMC *_class, STRING
944 *method_name)>
946 Find a method PMC for a named method, given the class PMC, current
947 interp, and name of the method.
949 This routine should use the current scope's method cache, if there is
950 one. If not, it creates a new method cache. Or, rather, it will when
951 we've got that bit working. For now it unconditionally goes and looks up
952 the name in the global stash.
954 =cut
958 PARROT_EXPORT
959 PARROT_CAN_RETURN_NULL
960 PARROT_WARN_UNUSED_RESULT
961 PMC *
962 Parrot_find_method_with_cache(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
964 ASSERT_ARGS(Parrot_find_method_with_cache)
965 UINTVAL type, bits;
967 Caches *mc;
968 Meth_cache_entry *e, *old;
970 PARROT_ASSERT(method_name != 0);
972 #if DISABLE_METH_CACHE
973 return Parrot_find_method_direct(interp, _class, method_name);
974 #endif
976 if (! PObj_constant_TEST(method_name))
977 return Parrot_find_method_direct(interp, _class, method_name);
979 mc = interp->caches;
980 type = _class->vtable->base_type;
981 bits = (((UINTVAL) method_name->strstart) >> 2) & TBL_SIZE_MASK;
983 if (type >= mc->mc_size) {
984 if (mc->idx) {
985 mc->idx = (Meth_cache_entry ***)mem_sys_realloc_zeroed(mc->idx,
986 sizeof (Meth_cache_entry ***) * (type + 1),
987 sizeof (Meth_cache_entry ***) * mc->mc_size);
989 else {
990 mc->idx = mem_allocate_n_zeroed_typed(type + 1, Meth_cache_entry**);
992 mc->mc_size = type + 1;
995 if (mc->idx[type] == NULL) {
996 mc->idx[type] = (Meth_cache_entry **)mem_sys_allocate_zeroed(
997 sizeof (Meth_cache_entry *) * TBL_SIZE);
1000 e = mc->idx[type][bits];
1001 old = NULL;
1003 while (e && e->strstart != method_name->strstart) {
1004 old = e;
1005 e = e->next;
1008 if (!e) {
1009 /* when here no or no correct entry was at [bits] */
1010 e = mem_allocate_typed(Meth_cache_entry);
1012 if (old)
1013 old->next = e;
1014 else
1015 mc->idx[type][bits] = e;
1017 e->pmc = Parrot_find_method_direct(interp, _class, method_name);
1018 e->next = NULL;
1019 e->strstart = method_name->strstart;
1022 return e->pmc;
1028 =item C<static void debug_trace_find_meth(PARROT_INTERP, const PMC *_class,
1029 const STRING *name, const PMC *sub)>
1031 Print some information about the search for a sub.
1033 =cut
1037 #ifdef NDEBUG
1038 # define TRACE_FM(i, c, m, sub)
1039 #else
1040 # define TRACE_FM(i, c, m, sub) \
1041 debug_trace_find_meth((i), (c), (m), (sub))
1043 static void
1044 debug_trace_find_meth(PARROT_INTERP, ARGIN(const PMC *_class),
1045 ARGIN(const STRING *name), ARGIN_NULLOK(const PMC *sub))
1047 ASSERT_ARGS(debug_trace_find_meth)
1048 STRING *class_name;
1049 const char *result;
1050 Interp *tracer;
1052 if (!Interp_trace_TEST(interp, PARROT_TRACE_FIND_METH_FLAG))
1053 return;
1055 if (PObj_is_class_TEST(_class)) {
1056 SLOTTYPE * const class_array = PMC_data_typed(_class, SLOTTYPE *);
1057 PMC * const class_name_pmc = get_attrib_num(class_array, PCD_CLASS_NAME);
1058 class_name = VTABLE_get_string(interp, class_name_pmc);
1060 else
1061 class_name = _class->vtable->whoami;
1063 if (sub) {
1064 if (sub->vtable->base_type == enum_class_NCI)
1065 result = "NCI";
1066 else
1067 result = "Sub";
1069 else
1070 result = "no";
1072 tracer = (interp->pdb && interp->pdb->debugger) ?
1073 interp->pdb->debugger :
1074 interp;
1075 Parrot_io_eprintf(tracer, "# find_method class '%Ss' method '%Ss': %s\n",
1076 class_name, name, result);
1079 #endif
1084 =item C<static PMC * find_method_direct_1(PARROT_INTERP, PMC *_class, STRING
1085 *method_name)>
1087 Find the method with the given name in the specified class.
1089 =cut
1093 PARROT_WARN_UNUSED_RESULT
1094 PARROT_CAN_RETURN_NULL
1095 static PMC *
1096 find_method_direct_1(PARROT_INTERP, ARGIN(PMC *_class),
1097 ARGIN(STRING *method_name))
1099 ASSERT_ARGS(find_method_direct_1)
1100 INTVAL i;
1102 PMC * const mro = _class->vtable->mro;
1103 const INTVAL n = VTABLE_elements(interp, mro);
1105 for (i = 0; i < n; ++i) {
1106 PMC *method, *ns;
1108 _class = VTABLE_get_pmc_keyed_int(interp, mro, i);
1109 ns = VTABLE_get_namespace(interp, _class);
1110 method = VTABLE_get_pmc_keyed_str(interp, ns, method_name);
1112 TRACE_FM(interp, _class, method_name, method);
1114 if (!PMC_IS_NULL(method))
1115 return method;
1118 TRACE_FM(interp, _class, method_name, NULL);
1119 return PMCNULL;
1125 =item C<static PMC* C3_merge(PARROT_INTERP, PMC *merge_list)>
1127 Merge together the MRO of the items in the list.
1129 =cut
1133 PARROT_WARN_UNUSED_RESULT
1134 PARROT_CAN_RETURN_NULL
1135 static PMC*
1136 C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
1138 ASSERT_ARGS(C3_merge)
1139 PMC *accepted = PMCNULL;
1140 PMC *result = PMCNULL;
1141 const int list_count = VTABLE_elements(interp, merge_list);
1142 int cand_count = 0;
1143 int i;
1145 /* Try and find something appropriate to add to the MRO - basically, the
1146 * first list head that is not in the tail of all the other lists. */
1147 for (i = 0; i < list_count; i++) {
1148 PMC * const cand_list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1150 PMC *cand_class;
1151 int reject = 0;
1152 int j;
1154 if (VTABLE_elements(interp, cand_list) == 0)
1155 continue;
1157 cand_class = VTABLE_get_pmc_keyed_int(interp, cand_list, 0);
1158 cand_count++;
1160 for (j = 0; j < list_count; j++) {
1161 /* Skip the current list. */
1162 if (j != i) {
1163 /* Is it in the tail? If so, reject. */
1164 PMC * const check_list =
1165 VTABLE_get_pmc_keyed_int(interp, merge_list, j);
1167 const int check_length = VTABLE_elements(interp, check_list);
1168 int k;
1170 for (k = 1; k < check_length; k++) {
1171 if (VTABLE_get_pmc_keyed_int(interp, check_list, k) ==
1172 cand_class) {
1173 reject = 1;
1174 break;
1180 /* If we didn't reject it, this candidate will do. */
1181 if (!reject) {
1182 accepted = cand_class;
1183 break;
1187 /* If we never found any candidates, return an empty list. */
1188 if (cand_count == 0)
1189 return pmc_new(interp, enum_class_ResizablePMCArray);
1191 /* If we didn't find anything to accept, error. */
1192 if (PMC_IS_NULL(accepted))
1193 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
1194 "Could not build C3 linearization: ambiguous hierarchy");
1196 /* Otherwise, remove what was accepted from the merge lists. */
1197 for (i = 0; i < list_count; i++) {
1199 PMC * const list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1200 const INTVAL list_count = VTABLE_elements(interp, list);
1201 INTVAL j;
1203 for (j = 0; j < list_count; j++) {
1204 if (VTABLE_get_pmc_keyed_int(interp, list, j) == accepted) {
1205 VTABLE_delete_keyed_int(interp, list, j);
1206 break;
1211 /* Need to merge what remains of the list, then put what was accepted on
1212 * the start of the list, and we're done. */
1213 result = C3_merge(interp, merge_list);
1214 VTABLE_unshift_pmc(interp, result, accepted);
1216 return result;
1222 =item C<PMC* Parrot_ComputeMRO_C3(PARROT_INTERP, PMC *_class)>
1224 Computes the C3 linearization for the given class. C3 is an algorithm to
1225 compute the method resolution order (MRO) of a class that is inheriting
1226 from multiple parent classes (multiple inheritance). C3 was first described
1227 by Barrett et al at:
1229 F<http://192.220.96.201/dylan/linearization-oopsla96.html>
1231 =cut
1235 PARROT_EXPORT
1236 PARROT_WARN_UNUSED_RESULT
1237 PARROT_CAN_RETURN_NULL
1238 PMC*
1239 Parrot_ComputeMRO_C3(PARROT_INTERP, ARGIN(PMC *_class))
1241 ASSERT_ARGS(Parrot_ComputeMRO_C3)
1243 PMC * const immediate_parents = VTABLE_inspect_str(interp, _class, CONST_STRING(interp, "parents"));
1244 PMC *merge_list;
1245 PMC *result;
1247 INTVAL i;
1248 INTVAL parent_count;
1250 /* Now get immediate parents list. */
1251 if (!immediate_parents)
1252 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_METHOD_NOT_FOUND,
1253 "Failed to get parents list from class!");
1255 parent_count = VTABLE_elements(interp, immediate_parents);
1257 if (parent_count == 0) {
1258 /* No parents - MRO just contains this class. */
1259 result = pmc_new(interp, enum_class_ResizablePMCArray);
1260 VTABLE_push_pmc(interp, result, _class);
1261 return result;
1264 /* Otherwise, need to do merge. For that, need linearizations of all of
1265 * our parents added to the merge list. */
1266 merge_list = PMCNULL;
1267 for (i = 0; i < parent_count; i++) {
1268 PMC * const lin = Parrot_ComputeMRO_C3(interp,
1269 VTABLE_get_pmc_keyed_int(interp, immediate_parents, i));
1271 if (PMC_IS_NULL(lin))
1272 return PMCNULL;
1274 /* instantiated lazily */
1275 if (PMC_IS_NULL(merge_list))
1276 merge_list = pmc_new(interp, enum_class_ResizablePMCArray);
1278 VTABLE_push_pmc(interp, merge_list, lin);
1281 /* Finally, need list of direct parents on the end of the merge list, then
1282 * we can merge. */
1283 VTABLE_push_pmc(interp, merge_list, immediate_parents);
1284 result = C3_merge(interp, merge_list);
1286 if (PMC_IS_NULL(result))
1287 return PMCNULL;
1289 /* Merged result needs this class on the start, and then we're done. */
1290 VTABLE_unshift_pmc(interp, result, _class);
1292 return result;
1298 =item C<void Parrot_ComposeRole(PARROT_INTERP, PMC *role, PMC *exclude, int
1299 got_exclude, PMC *alias, int got_alias, PMC *methods_hash, PMC *roles_list)>
1301 Used by the Class and Object PMCs internally to compose a role into either of
1302 them. The C<role> parameter is the role that we are composing into the class
1303 or role. C<methods_hash> is the hash of method names to invokable PMCs that
1304 contains the methods the class or role has. C<roles_list> is the list of roles
1305 the the class or method does.
1307 The C<role> parameter is only dealt with by its external interface. Whether
1308 this routine is usable by any other object system implemented in Parrot very
1309 much depends on how closely the role composition semantics they want are to
1310 the default implementation.
1312 =cut
1316 PARROT_EXPORT
1317 void
1318 Parrot_ComposeRole(PARROT_INTERP, ARGIN(PMC *role),
1319 ARGIN(PMC *exclude), int got_exclude,
1320 ARGIN(PMC *alias), int got_alias,
1321 ARGIN(PMC *methods_hash), ARGIN(PMC *roles_list))
1323 ASSERT_ARGS(Parrot_ComposeRole)
1324 PMC *methods;
1325 PMC *methods_iter;
1326 PMC *roles_of_role;
1327 PMC *proposed_add_methods;
1329 INTVAL roles_of_role_count;
1330 INTVAL i;
1332 /* Check we have not already composed the role; if so, just ignore it. */
1333 INTVAL roles_count = VTABLE_elements(interp, roles_list);
1335 for (i = 0; i < roles_count; i++)
1336 if (VTABLE_get_pmc_keyed_int(interp, roles_list, i) == role)
1337 return;
1339 /* Get the methods from the role. */
1340 Parrot_PCCINVOKE(interp, role, CONST_STRING(interp, "methods"), "->P", &methods);
1342 if (PMC_IS_NULL(methods))
1343 return;
1345 /* We need to check for conflicts before we do the composition. We
1346 * put each method that would be OK to add into a proposal list, and
1347 * bail out right away if we find a problem. */
1348 proposed_add_methods = pmc_new(interp, enum_class_Hash);
1349 methods_iter = VTABLE_get_iter(interp, methods);
1351 while (VTABLE_get_bool(interp, methods_iter)) {
1352 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1353 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp, methods,
1354 method_name);
1356 /* Need to find the name we'll check for a conflict on. */
1357 int excluded = 0;
1359 /* Check if it's in the exclude list. */
1360 if (got_exclude) {
1361 const int exclude_count = VTABLE_elements(interp, exclude);
1363 for (i = 0; i < exclude_count; i++) {
1364 const STRING * const check =
1365 VTABLE_get_string_keyed_int(interp, exclude, i);
1367 if (Parrot_str_equal(interp, check, method_name)) {
1368 excluded = 1;
1369 break;
1374 /* If we weren't excluded... */
1375 if (!excluded) {
1376 /* Is there a method with this name already in the class? */
1378 if (VTABLE_exists_keyed_str(interp, methods_hash, method_name)) {
1379 /* Conflicts with something already in the class, unless it's a
1380 * multi-method. */
1381 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, method_name);
1382 if (PMC_IS_NULL(cur_entry) || !VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1383 Parrot_ex_throw_from_c_args(interp, NULL,
1384 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1385 "A conflict occurred during role composition "
1386 "due to method '%S'.", method_name);
1389 /* What about a conflict with ourslef? */
1390 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1391 method_name))
1392 /* Something very weird is going on. */
1393 Parrot_ex_throw_from_c_args(interp, NULL,
1394 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1395 "A conflict occurred during role composition;"
1396 " the method '%S' from the role managed to conflict "
1397 "with itself somehow.", method_name);
1399 /* If we got here, no conflicts! Add method to the "to compose"
1400 * list. */
1401 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1402 method_name, cur_method);
1405 /* Now see if we've got an alias. */
1406 if (got_alias && VTABLE_exists_keyed_str(interp, alias, method_name)) {
1407 /* Got one. Get name to alias it to. */
1408 STRING * const alias_name = VTABLE_get_string_keyed_str(interp,
1409 alias, method_name);
1411 /* Is there a method with this name already in the class? If it's
1412 * not a multi-method, error. */
1413 if (VTABLE_exists_keyed_str(interp, methods_hash, alias_name)) {
1414 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, alias_name);
1415 if (PMC_IS_NULL(cur_entry) || !VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1416 /* Conflicts with something already in the class. */
1417 Parrot_ex_throw_from_c_args(interp, NULL,
1418 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1419 "A conflict occurred during role composition"
1420 " due to the aliasing of '%S' to '%S'.",
1421 method_name, alias_name);
1424 /* What about a conflict with ourslef? */
1425 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1426 alias_name))
1427 Parrot_ex_throw_from_c_args(interp, NULL,
1428 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1429 "A conflict occurred during role composition"
1430 " due to the aliasing of '%S' to '%S' (role already has"
1431 " a method '%S').", method_name, alias_name, alias_name);
1433 /* If we get here, no conflicts! Add method to the "to compose"
1434 * list with its alias. */
1435 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1436 alias_name, cur_method);
1440 /* If we get here, we detected no conflicts. Go ahead and compose the
1441 * methods. */
1442 methods_iter = VTABLE_get_iter(interp, proposed_add_methods);
1444 while (VTABLE_get_bool(interp, methods_iter)) {
1445 /* Get current method and its name. */
1446 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1447 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp,
1448 proposed_add_methods, method_name);
1450 /* Add it to the methods of the class. */
1451 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, method_name);
1452 if (VTABLE_isa(interp, cur_method, CONST_STRING(interp, "MultiSub"))) {
1453 /* The thing we're adding is a multi-sub, but is the thing in the
1454 * class already a multi-sub? */
1455 if (!PMC_IS_NULL(cur_entry) && VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub"))) {
1456 /* Class already has a multi-sub; need to merge our methods into it. */
1457 const INTVAL num_subs = VTABLE_elements(interp, cur_method);
1458 INTVAL j;
1459 for (j = 0; j < num_subs; j++)
1460 VTABLE_push_pmc(interp, cur_entry, VTABLE_get_pmc_keyed_int(interp,
1461 cur_method, j));
1463 else {
1464 /* It's not, and we didn't conflict so must be no entry. Just stick it in. */
1465 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1468 else {
1469 /* Are we adding into a multi-sub? */
1470 if (!PMC_IS_NULL(cur_entry) && VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1471 VTABLE_push_pmc(interp, cur_entry, cur_method);
1472 else
1473 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1477 /* Add this role to the roles list. */
1478 VTABLE_push_pmc(interp, roles_list, role);
1479 roles_count++;
1481 /* As a result of composing this role, we will also now do the roles
1482 * that it did itself. Note that we already have the correct methods
1483 * as roles "flatten" the methods they get from other roles into their
1484 * own method list. */
1485 Parrot_PCCINVOKE(interp, role, CONST_STRING(interp, "roles"), "->P", &roles_of_role);
1486 roles_of_role_count = VTABLE_elements(interp, roles_of_role);
1488 for (i = 0; i < roles_of_role_count; i++) {
1489 /* Only add if we don't already have it in the list. */
1490 PMC * const cur_role = VTABLE_get_pmc_keyed_int(interp,
1491 roles_of_role, i);
1492 INTVAL j;
1494 for (j = 0; j < roles_count; j++) {
1495 if (VTABLE_get_pmc_keyed_int(interp, roles_list, j) == cur_role) {
1496 /* We ain't be havin' it. */
1497 VTABLE_push_pmc(interp, roles_list, cur_role);
1506 =back
1508 =head1 SEE ALSO
1510 F<include/parrot/oo.h>, F<include/parrot/oo_private.h>,
1511 F<docs/pdds/pdd15_objects.pod>.
1513 =cut
1518 * Local variables:
1519 * c-file-style: "parrot"
1520 * End:
1521 * vim: expandtab shiftwidth=4: