tagged release 0.7.1
[parrot.git] / src / oo.c
blob2900def7cca527f58be2d5a849659d8a92c4f10a
1 /*
2 Copyright (C) 2007-2008, The Perl 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"
27 #include "oo.str"
29 /* HEADERIZER HFILE: include/parrot/oo.h */
31 /* HEADERIZER BEGIN: static */
32 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
34 PARROT_WARN_UNUSED_RESULT
35 PARROT_CAN_RETURN_NULL
36 static PMC* C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
37 __attribute__nonnull__(1)
38 __attribute__nonnull__(2);
40 static void debug_trace_find_meth(PARROT_INTERP,
41 ARGIN(const PMC *_class),
42 ARGIN(const STRING *name),
43 ARGIN_NULLOK(const PMC *sub))
44 __attribute__nonnull__(1)
45 __attribute__nonnull__(2)
46 __attribute__nonnull__(3);
48 static void fail_if_type_exists(PARROT_INTERP, ARGIN(PMC *name))
49 __attribute__nonnull__(1)
50 __attribute__nonnull__(2);
52 PARROT_WARN_UNUSED_RESULT
53 PARROT_CAN_RETURN_NULL
54 static PMC * find_method_direct_1(PARROT_INTERP,
55 ARGIN(PMC *_class),
56 ARGIN(STRING *method_name))
57 __attribute__nonnull__(1)
58 __attribute__nonnull__(2)
59 __attribute__nonnull__(3);
61 PARROT_WARN_UNUSED_RESULT
62 PARROT_CAN_RETURN_NULL
63 static PMC* find_vtable_meth_ns(PARROT_INTERP,
64 ARGIN(PMC *ns),
65 INTVAL vtable_index)
66 __attribute__nonnull__(1)
67 __attribute__nonnull__(2);
69 PARROT_WARN_UNUSED_RESULT
70 PARROT_CAN_RETURN_NULL
71 static PMC* get_init_meth(PARROT_INTERP,
72 ARGIN(PMC *_class),
73 ARGIN(STRING *prop_str),
74 ARGOUT(STRING **meth_str))
75 __attribute__nonnull__(1)
76 __attribute__nonnull__(2)
77 __attribute__nonnull__(3)
78 __attribute__nonnull__(4)
79 FUNC_MODIFIES(*meth_str);
81 static void invalidate_all_caches(PARROT_INTERP)
82 __attribute__nonnull__(1);
84 static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)
85 __attribute__nonnull__(1);
87 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
88 /* HEADERIZER END: static */
92 =item C<void Parrot_oo_extract_methods_from_namespace>
94 Extract methods and vtable overrides from the given namespace and insert them
95 into the class.
97 =cut
101 void
102 Parrot_oo_extract_methods_from_namespace(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *ns))
104 PMC *methods, *vtable_overrides;
106 /* Pull in methods from the namespace, if any. */
107 if (PMC_IS_NULL(ns))
108 return;
110 /* Import any methods. */
111 Parrot_PCCINVOKE(interp, ns,
112 CONST_STRING(interp, "get_associated_methods"), "->P", &methods);
114 if (!PMC_IS_NULL(methods)) {
115 PMC * const iter = VTABLE_get_iter(interp, methods);
117 while (VTABLE_get_bool(interp, iter)) {
118 STRING * const meth_name = VTABLE_shift_string(interp, iter);
119 PMC * const meth_sub = VTABLE_get_pmc_keyed_str(interp, methods,
120 meth_name);
121 VTABLE_add_method(interp, self, meth_name, meth_sub);
125 /* Import any vtable methods. */
126 Parrot_PCCINVOKE(interp, ns,
127 CONST_STRING(interp, "get_associated_vtable_methods"), "->P", &vtable_overrides);
129 if (!PMC_IS_NULL(vtable_overrides)) {
130 PMC * const iter = VTABLE_get_iter(interp, vtable_overrides);
131 while (VTABLE_get_bool(interp, iter)) {
132 STRING * const vtable_index_str = VTABLE_shift_string(interp, iter);
133 PMC * const vtable_sub = VTABLE_get_pmc_keyed_str(interp,
134 vtable_overrides, vtable_index_str);
136 /* Look up the name of the vtable function from the index. */
137 const INTVAL vtable_index = string_to_int(interp, vtable_index_str);
138 const char * const meth_c = Parrot_vtable_slot_names[vtable_index];
139 STRING *vtable_name = string_from_cstring(interp, meth_c, 0);
141 /* Strip leading underscores in the vtable name */
142 if (string_str_index(interp, vtable_name,
143 CONST_STRING(interp, "__"), 0) == 0) {
144 vtable_name = string_substr(interp, vtable_name, 2,
145 string_length(interp, vtable_name) - 2, NULL, 0);
148 VTABLE_add_vtable_override(interp, self, vtable_name, vtable_sub);
156 =item C<PMC * Parrot_oo_get_namespace>
158 Lookup a namespace object from a class PMC.
160 =cut
164 PARROT_CAN_RETURN_NULL
165 PARROT_WARN_UNUSED_RESULT
166 PMC *
167 Parrot_oo_get_namespace(SHIM_INTERP, ARGIN(const PMC *classobj))
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>
183 Lookup a class object from a namespace, string, or key PMC.
185 =cut
189 PARROT_API
190 PARROT_CAN_RETURN_NULL
191 PARROT_WARN_UNUSED_RESULT
192 PMC *
193 Parrot_oo_get_class(PARROT_INTERP, ARGIN(PMC *key))
195 PMC *classobj = PMCNULL;
197 if (PObj_is_class_TEST(key))
198 classobj = key;
199 else {
200 /* Fast select of behavior based on type of the lookup key */
201 switch (key->vtable->base_type) {
202 case enum_class_NameSpace:
203 classobj = VTABLE_get_class(interp, key);
204 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 CONTEXT(interp)->current_HLL);
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 (PMC_IS_NULL(classobj)) {
225 /* Look up a low-level class and create a proxy */
226 const INTVAL type = pmc_type(interp, VTABLE_get_string(interp, key));
228 /* Reject invalid type numbers */
229 if (type > interp->n_vtable_max || type <= 0)
230 return PMCNULL;
231 else {
232 PMC * const type_num = pmc_new(interp, enum_class_Integer);
233 VTABLE_set_integer_native(interp, type_num, type);
234 classobj = pmc_new_init(interp, enum_class_PMCProxy, type_num);
238 return classobj;
244 =item C<PMC * Parrot_oo_get_class_str>
246 Lookup a class object from a builtin string.
248 =cut
252 PARROT_API
253 PARROT_CAN_RETURN_NULL
254 PARROT_WARN_UNUSED_RESULT
255 PMC *
256 Parrot_oo_get_class_str(PARROT_INTERP, ARGIN(STRING *name))
258 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp, interp->HLL_namespace,
259 CONTEXT(interp)->current_HLL);
260 PMC * const ns = Parrot_get_namespace_keyed_str(interp, hll_ns, name);
261 PMC * const _class = PMC_IS_NULL(ns)
262 ? PMCNULL : VTABLE_get_class(interp, ns);
264 /* Look up a low-level class and create a proxy */
265 if (PMC_IS_NULL(_class)) {
266 const INTVAL type = pmc_type(interp, name);
268 /* Reject invalid type numbers */
269 if (type > interp->n_vtable_max || type <= 0)
270 return PMCNULL;
271 else {
272 PMC * const type_num = pmc_new(interp, enum_class_Integer);
273 VTABLE_set_integer_native(interp, type_num, type);
274 return pmc_new_init(interp, enum_class_PMCProxy, type_num);
278 return _class;
284 =item C<PMC * Parrot_oo_newclass_from_str>
286 Create a new class object from a string name.
288 =cut
292 PARROT_CAN_RETURN_NULL
293 PARROT_WARN_UNUSED_RESULT
294 PMC *
295 Parrot_oo_newclass_from_str(PARROT_INTERP, ARGIN(STRING *name))
297 PMC * const namearg = pmc_new(interp, enum_class_String);
298 PMC *namehash = pmc_new(interp, enum_class_Hash);
299 PMC *classobj;
301 VTABLE_set_string_native(interp, namearg, name);
302 VTABLE_set_pmc_keyed_str(interp, namehash, CONST_STRING(interp, "name"), namearg);
304 classobj = pmc_new_init(interp, enum_class_Class, namehash);
306 PARROT_ASSERT(classobj);
307 return classobj;
313 =item C<PMC * Parrot_oo_find_vtable_override_for_class>
315 Lookup a vtable override in a specific class object.
317 =cut
321 PARROT_CAN_RETURN_NULL
322 PARROT_WARN_UNUSED_RESULT
323 PMC *
324 Parrot_oo_find_vtable_override_for_class(PARROT_INTERP,
325 ARGIN(PMC *classobj), ARGIN(STRING *name))
327 Parrot_Class_attributes *class_info;
328 PARROT_ASSERT(PObj_is_class_TEST(classobj));
330 class_info = PARROT_CLASS(classobj);
331 return VTABLE_get_pmc_keyed_str(interp, class_info->vtable_overrides, name);
337 =item C<PMC * Parrot_oo_find_vtable_override>
339 Lookup a vtable override in a class, including any vtable overrides inherited
340 from parents.
342 =cut
346 PARROT_CAN_RETURN_NULL
347 PARROT_WARN_UNUSED_RESULT
348 PMC *
349 Parrot_oo_find_vtable_override(PARROT_INTERP,
350 ARGIN(PMC *classobj), ARGIN(STRING *name))
352 Parrot_Class_attributes * const _class = PARROT_CLASS(classobj);
354 if (VTABLE_exists_keyed_str(interp, _class->parent_overrides, name))
355 return VTABLE_get_pmc_keyed_str(interp, _class->parent_overrides, name);
356 else {
357 /* Walk and search for the vtable method. */
358 const INTVAL num_classes = VTABLE_elements(interp, _class->all_parents);
359 PMC *result = PMCNULL;
360 INTVAL i;
362 for (i = 0; i < num_classes; i++) {
363 /* Get the class. */
364 PMC * const cur_class =
365 VTABLE_get_pmc_keyed_int(interp, _class->all_parents, i);
367 result = Parrot_oo_find_vtable_override_for_class(interp,
368 cur_class, name);
370 if (!PMC_IS_NULL(result))
371 break;
374 VTABLE_set_pmc_keyed_str(interp, _class->parent_overrides, name, result);
376 return result;
383 =item C<INTVAL Parrot_get_vtable_index>
385 Return index if C<name> is a valid vtable slot name.
387 =cut
391 PARROT_API
392 INTVAL
393 Parrot_get_vtable_index(PARROT_INTERP, ARGIN(const STRING *name))
395 char * const name_c = string_to_cstring(interp, name);
397 /* some of the first "slots" don't have names. skip 'em. */
398 INTVAL low = PARROT_VTABLE_LOW;
399 INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW;
401 while (low < high) {
402 const INTVAL mid = (low + high) / 2;
403 const char * const meth_c = Parrot_vtable_slot_names[mid];
405 /* RT#45965 slot_names still have __ in front */
406 const INTVAL cmp = strcmp(name_c, meth_c + 2);
408 if (cmp == 0) {
409 string_cstring_free(name_c);
410 return mid;
412 else if (cmp > 0)
413 low = mid + 1;
414 else
415 high = mid;
418 string_cstring_free(name_c);
420 return -1;
426 =item C<static PMC* find_vtable_meth_ns>
428 Return Sub PMC if a method with the vtable name exists in ns
430 =cut
434 PARROT_WARN_UNUSED_RESULT
435 PARROT_CAN_RETURN_NULL
436 static PMC*
437 find_vtable_meth_ns(PARROT_INTERP, ARGIN(PMC *ns), INTVAL vtable_index)
439 return VTABLE_get_pmc_keyed_int(interp, ns, vtable_index);
445 =item C<STRING* readable_name>
447 Given a String or Key PMC return the STRING* representation
449 RT#45967 this function, key_set_to_string, and the key PMC get_repr should be
450 consolidated
452 =cut
456 PARROT_API
457 PARROT_WARN_UNUSED_RESULT
458 PARROT_CANNOT_RETURN_NULL
459 STRING*
460 readable_name(PARROT_INTERP, ARGIN(PMC *name))
462 PMC *array;
464 if (name->vtable->base_type == enum_class_String)
465 return VTABLE_get_string(interp, name);
467 array = pmc_new(interp, enum_class_ResizableStringArray);
469 PARROT_ASSERT(name->vtable->base_type == enum_class_Key);
471 while (name) {
472 VTABLE_push_string(interp, array, VTABLE_get_string(interp, name));
473 name = key_next(interp, name);
476 return string_join(interp, CONST_STRING(interp, ";"), array);
482 =item C<const char* Parrot_MMD_method_name>
484 Return the method name for the given MMD enum.
486 =cut
490 PARROT_API
491 PARROT_PURE_FUNCTION
492 PARROT_CAN_RETURN_NULL
493 const char*
494 Parrot_MMD_method_name(SHIM_INTERP, INTVAL idx)
496 PARROT_ASSERT(idx >= 0);
498 if (idx >= MMD_USER_FIRST)
499 return NULL;
501 return Parrot_mmd_func_names[idx];
507 =item C<INTVAL Parrot_MMD_method_idx>
509 Return the MMD function number for method name or -1 on failure.
511 RT#45973 allow dynamic expansion at runtime.
513 =cut
517 PARROT_API
518 PARROT_PURE_FUNCTION
519 INTVAL
520 Parrot_MMD_method_idx(SHIM_INTERP, ARGIN(const char *name))
522 INTVAL i;
524 for (i = 0; i < MMD_USER_FIRST; ++i) {
525 if (STREQ(Parrot_mmd_func_names[i], name))
526 return i;
529 return -1;
535 =item C<PMC * Parrot_class_lookup>
537 Looks for the class named C<class_name> and returns it if it exists.
538 Otherwise it returns C<PMCNULL>.
540 =cut
544 PARROT_API
545 PARROT_CAN_RETURN_NULL
546 PARROT_WARN_UNUSED_RESULT
547 PMC *
548 Parrot_class_lookup(PARROT_INTERP, ARGIN(STRING *class_name))
550 const INTVAL type = pmc_type(interp, class_name);
551 PMC *pmc;
553 if (type <= 0)
554 return PMCNULL;
556 pmc = interp->vtables[type]->pmc_class;
557 PARROT_ASSERT(pmc);
558 return pmc;
564 =item C<PMC * Parrot_class_lookup_p>
566 Looks for the class named C<class_name> and returns it if it exists.
567 Otherwise it returns C<PMCNULL>.
569 =cut
573 PARROT_CAN_RETURN_NULL
574 PARROT_WARN_UNUSED_RESULT
575 PMC *
576 Parrot_class_lookup_p(PARROT_INTERP, ARGIN(PMC *class_name))
578 const INTVAL type = pmc_type_p(interp, class_name);
579 PMC *pmc;
581 if (type <= 0)
582 return PMCNULL;
584 pmc = interp->vtables[type]->pmc_class;
585 PARROT_ASSERT(pmc);
586 return pmc;
592 =item C<static void fail_if_type_exists>
594 This function throws an exception if a PMC or class with the same name *
595 already exists in the global type registry. The global type registry
596 will go away eventually, but this allows the new object metamodel to
597 interact with the old one until it does.
599 =cut
603 static void
604 fail_if_type_exists(PARROT_INTERP, ARGIN(PMC *name))
606 INTVAL type;
608 PMC * const classname_hash = interp->class_hash;
609 PMC * const type_pmc = (PMC *)VTABLE_get_pointer_keyed(interp,
610 classname_hash, name);
612 if (PMC_IS_NULL(type_pmc)
613 || type_pmc->vtable->base_type == enum_class_NameSpace)
614 type = 0;
615 else
616 type = VTABLE_get_integer(interp, type_pmc);
618 if (type > enum_type_undef)
619 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
620 "Class %Ss already registered!\n",
621 string_escape_string(interp, VTABLE_get_string(interp, name)));
623 if (type < enum_type_undef)
624 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
625 "native type with name '%s' already exists - "
626 "can't register Class", data_types[type].name);
632 =item C<INTVAL Parrot_oo_register_type>
634 This function registers a type in the global registry, first checking if it
635 already exists. The global type registry will go away eventually, but this
636 allows the new object metamodel to interact with the old one until it does.
638 =cut
642 PARROT_WARN_UNUSED_RESULT
643 INTVAL
644 Parrot_oo_register_type(PARROT_INTERP, ARGIN(PMC *name))
646 INTVAL type;
647 PMC *classname_hash, *item;
649 fail_if_type_exists(interp, name);
651 /* Type doesn't exist, so go ahead and register it. Lock interpreter so
652 * pt_shared_fixup() can safely do a type lookup. */
653 LOCK_INTERPRETER(interp);
654 classname_hash = interp->class_hash;
656 type = interp->n_vtable_max++;
658 /* Have we overflowed the table? */
659 if (type >= interp->n_vtable_alloced)
660 parrot_realloc_vtables(interp);
662 /* set entry in name->type hash */
663 item = pmc_new(interp, enum_class_Integer);
664 PMC_int_val(item) = type;
666 VTABLE_set_pmc_keyed(interp, classname_hash, name, item);
667 UNLOCK_INTERPRETER(interp);
669 return type;
675 =item C<static PMC* get_init_meth>
677 RT#48260: Not yet documented!!!
679 =cut
683 PARROT_WARN_UNUSED_RESULT
684 PARROT_CAN_RETURN_NULL
685 static PMC*
686 get_init_meth(PARROT_INTERP, ARGIN(PMC *_class),
687 ARGIN(STRING *prop_str), ARGOUT(STRING **meth_str))
689 STRING *meth;
690 HashBucket *b;
691 PMC *props, *ns, *method;
693 *meth_str = NULL;
694 #if 0
695 PMC * const prop = VTABLE_getprop(interp, _class, prop_str);
696 if (!VTABLE_defined(interp, prop))
697 return PMCNULL;
698 meth = VTABLE_get_string(interp, prop);
699 #else
700 props = PMC_metadata(_class);
701 if (!props)
702 return PMCNULL;
704 b = parrot_hash_get_bucket(interp,
705 (Hash*) PMC_struct_val(props), prop_str);
706 if (!b)
707 return PMCNULL;
709 meth = PMC_str_val((PMC*) b->value);
710 #endif
712 *meth_str = meth;
713 ns = VTABLE_get_namespace(interp, _class);
714 method = VTABLE_get_pmc_keyed_str(interp, ns, meth);
716 return method;
721 =item C<PMC * Parrot_remove_parent>
723 This currently does nothing but return C<PMCNULL>.
724 RT#50646
726 =cut
730 PARROT_API
731 PARROT_IGNORABLE_RESULT
732 PARROT_CAN_RETURN_NULL
733 PMC *
734 Parrot_remove_parent(PARROT_INTERP, ARGIN(PMC *removed_class),
735 ARGIN(PMC *existing_class))
737 UNUSED(interp);
738 UNUSED(removed_class);
739 UNUSED(existing_class);
741 return PMCNULL;
747 =item C<void mark_object_cache>
749 Marks all PMCs in the object method cache as live. This shouldn't strictly be
750 necessary, as they're likely all reachable from namespaces and classes, but
751 it's unlikely to hurt anything except mark phase performance.
753 =cut
757 #define TBL_SIZE_MASK 0x1ff /* x bits 2..10 */
758 #define TBL_SIZE (1 + TBL_SIZE_MASK)
760 void
761 mark_object_cache(PARROT_INTERP)
763 Caches * const mc = interp->caches;
764 UINTVAL type, entry;
766 if (!mc)
767 return;
769 for (type = 0; type < mc->mc_size; type++) {
770 if (!mc->idx[type])
771 continue;
773 for (entry = 0; entry < TBL_SIZE; ++entry) {
774 Meth_cache_entry *e = mc->idx[type][entry];
775 while (e) {
776 pobject_lives(interp, (PObj *)e->pmc);
777 e = e->next;
786 =item C<void init_object_cache>
788 Allocate memory for object cache.
790 =cut
794 void
795 init_object_cache(PARROT_INTERP)
797 Caches * const mc = interp->caches = mem_allocate_zeroed_typed(Caches);
798 mc->idx = NULL;
804 =item C<void destroy_object_cache>
806 RT#48260: Not yet documented!!!
808 =cut
812 void
813 destroy_object_cache(PARROT_INTERP)
815 UINTVAL i;
816 Caches * const mc = interp->caches;
818 /* mc->idx[type][bits] = e; */
819 for (i = 0; i < mc->mc_size; i++) {
820 if (mc->idx[i])
821 invalidate_type_caches(interp, i);
824 mem_sys_free(mc->idx);
825 mem_sys_free(mc);
831 =item C<static void invalidate_type_caches>
833 RT#48260: Not yet documented!!!
835 =cut
839 static void
840 invalidate_type_caches(PARROT_INTERP, UINTVAL type)
842 Caches * const mc = interp->caches;
843 INTVAL i;
845 if (!mc)
846 return;
848 /* is it a valid entry */
849 if (type >= mc->mc_size || !mc->idx[type])
850 return;
852 for (i = 0; i < TBL_SIZE; ++i) {
853 Meth_cache_entry *e = mc->idx[type][i];
854 while (e) {
855 Meth_cache_entry * const next = e->next;
856 mem_sys_free(e);
857 e = next;
861 mem_sys_free(mc->idx[type]);
862 mc->idx[type] = NULL;
868 =item C<static void invalidate_all_caches>
870 RT#48260: Not yet documented!!!
872 =cut
876 static void
877 invalidate_all_caches(PARROT_INTERP)
879 UINTVAL i;
880 for (i = 1; i < (UINTVAL)interp->n_vtable_max; ++i)
881 invalidate_type_caches(interp, i);
887 =item C<void Parrot_invalidate_method_cache>
889 Clear method cache for the given class. If class is NULL, caches for
890 all classes are invalidated.
892 =cut
896 PARROT_API
897 void
898 Parrot_invalidate_method_cache(PARROT_INTERP, ARGIN_NULLOK(STRING *_class), ARGIN(STRING *meth))
900 INTVAL type;
902 /* during interp creation and NCI registration the class_hash
903 * isn't yet up */
904 if (!interp->class_hash)
905 return;
907 if (interp->resume_flag & RESUME_INITIAL)
908 return;
910 if (!_class) {
911 invalidate_all_caches(interp);
912 return;
915 type = pmc_type(interp, _class);
917 if (type == 0)
918 invalidate_all_caches(interp);
919 else if (type > 0)
920 invalidate_type_caches(interp, (UINTVAL)type);
925 * quick'n'dirty method cache
926 * RT#45987: use a hash if method_name is not constant
927 * i.e. from obj.$Sreg(args)
928 * If this hash is implemented mark it during DOD
933 =item C<PMC * Parrot_find_method_direct>
935 Find a method PMC for a named method, given the class PMC, current
936 interpreter, and name of the method. Don't use a possible method cache.
938 =cut
942 PARROT_API
943 PARROT_CAN_RETURN_NULL
944 PARROT_WARN_UNUSED_RESULT
945 PMC *
946 Parrot_find_method_direct(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
948 PMC * const found = find_method_direct_1(interp, _class, method_name);
950 if (!PMC_IS_NULL(found))
951 return found;
954 if (!string_equal(interp, method_name, CONST_STRING(interp, "__get_string")))
955 return find_method_direct_1(interp, _class,
956 CONST_STRING(interp, "__get_repr"));
958 return PMCNULL;
964 =item C<PMC * Parrot_find_method_with_cache>
966 Find a method PMC for a named method, given the class PMC, current
967 interp, and name of the method.
969 This routine should use the current scope's method cache, if there is
970 one. If not, it creates a new method cache. Or, rather, it will when
971 we've got that bit working. For now it unconditionally goes and looks up
972 the name in the global stash.
974 =cut
978 PARROT_API
979 PARROT_CAN_RETURN_NULL
980 PARROT_WARN_UNUSED_RESULT
981 PMC *
982 Parrot_find_method_with_cache(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
984 UINTVAL type, bits;
986 Caches *mc;
987 Meth_cache_entry *e, *old;
989 PARROT_ASSERT(method_name != 0);
991 #if DISABLE_METH_CACHE
992 return Parrot_find_method_direct(interp, _class, method_name);
993 #endif
995 if (! PObj_constant_TEST(method_name))
996 return Parrot_find_method_direct(interp, _class, method_name);
998 mc = interp->caches;
999 type = _class->vtable->base_type;
1000 bits = (((UINTVAL) method_name->strstart) >> 2) & TBL_SIZE_MASK;
1002 if (type >= mc->mc_size) {
1003 if (mc->idx) {
1004 mc->idx = (Meth_cache_entry ***)mem_sys_realloc_zeroed(mc->idx,
1005 sizeof (Meth_cache_entry ***) * (type + 1),
1006 sizeof (Meth_cache_entry ***) * mc->mc_size);
1008 else {
1009 mc->idx = (Meth_cache_entry ***)mem_sys_allocate_zeroed(
1010 sizeof (Meth_cache_entry ***) * (type + 1));
1012 mc->mc_size = type + 1;
1015 if (!mc->idx[type]) {
1016 mc->idx[type] = (Meth_cache_entry **)mem_sys_allocate_zeroed(
1017 sizeof (Meth_cache_entry *) * TBL_SIZE);
1020 e = mc->idx[type][bits];
1021 old = NULL;
1023 while (e && e->strstart != method_name->strstart) {
1024 old = e;
1025 e = e->next;
1028 if (!e) {
1029 /* when here no or no correct entry was at [bits] */
1030 e = mem_allocate_typed(Meth_cache_entry);
1032 if (old)
1033 old->next = e;
1034 else
1035 mc->idx[type][bits] = e;
1037 e->pmc = Parrot_find_method_direct(interp, _class, method_name);
1038 e->next = NULL;
1039 e->strstart = method_name->strstart;
1042 return e->pmc;
1048 =item C<static void debug_trace_find_meth>
1050 RT#48260: Not yet documented!!!
1052 =cut
1056 #ifdef NDEBUG
1057 # define TRACE_FM(i, c, m, sub)
1058 #else
1059 # define TRACE_FM(i, c, m, sub) \
1060 debug_trace_find_meth((i), (c), (m), (sub))
1062 static void
1063 debug_trace_find_meth(PARROT_INTERP, ARGIN(const PMC *_class),
1064 ARGIN(const STRING *name), ARGIN_NULLOK(const PMC *sub))
1066 STRING *class_name;
1067 const char *result;
1068 Interp *tracer;
1070 if (!Interp_trace_TEST(interp, PARROT_TRACE_FIND_METH_FLAG))
1071 return;
1073 if (PObj_is_class_TEST(_class)) {
1074 SLOTTYPE * const class_array = PMC_data_typed(_class, SLOTTYPE *);
1075 PMC *const class_name_pmc = get_attrib_num(class_array, PCD_CLASS_NAME);
1076 class_name = PMC_str_val(class_name_pmc);
1078 else
1079 class_name = _class->vtable->whoami;
1081 if (sub) {
1082 if (sub->vtable->base_type == enum_class_NCI)
1083 result = "NCI";
1084 else
1085 result = "Sub";
1087 else
1088 result = "no";
1090 tracer = interp->debugger ? interp->debugger : interp;
1091 PIO_eprintf(tracer, "# find_method class '%Ss' method '%Ss': %s\n",
1092 class_name, name, result);
1095 #endif
1100 =item C<static PMC * find_method_direct_1>
1102 RT#48260: Not yet documented!!!
1104 =cut
1108 PARROT_WARN_UNUSED_RESULT
1109 PARROT_CAN_RETURN_NULL
1110 static PMC *
1111 find_method_direct_1(PARROT_INTERP, ARGIN(PMC *_class),
1112 ARGIN(STRING *method_name))
1114 INTVAL i;
1116 PMC * const mro = _class->vtable->mro;
1117 const INTVAL n = VTABLE_elements(interp, mro);
1119 for (i = 0; i < n; ++i) {
1120 PMC *method, *ns;
1122 _class = VTABLE_get_pmc_keyed_int(interp, mro, i);
1123 ns = VTABLE_get_namespace(interp, _class);
1124 method = VTABLE_get_pmc_keyed_str(interp, ns, method_name);
1126 TRACE_FM(interp, _class, method_name, method);
1128 if (!PMC_IS_NULL(method))
1129 return method;
1132 TRACE_FM(interp, _class, method_name, NULL);
1133 return PMCNULL;
1137 /* ************************************************************************ */
1138 /* ********* BELOW HERE IS NEW PPD15 IMPLEMENTATION RELATED STUFF ********* */
1139 /* ************************************************************************ */
1143 =item C<static PMC* C3_merge>
1145 RT#48260: Not yet documented!!!
1147 =cut
1151 PARROT_WARN_UNUSED_RESULT
1152 PARROT_CAN_RETURN_NULL
1153 static PMC*
1154 C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
1156 PMC *accepted = PMCNULL;
1157 PMC *result = pmc_new(interp, enum_class_ResizablePMCArray);
1158 const int list_count = VTABLE_elements(interp, merge_list);
1159 int cand_count = 0;
1160 int i;
1162 /* Try and find something appropriate to add to the MRO - basically, the
1163 * first list head that is not in the tail of all the other lists. */
1164 for (i = 0; i < list_count; i++) {
1165 PMC * const cand_list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1167 PMC *cand_class;
1168 int reject = 0;
1169 int j;
1171 if (VTABLE_elements(interp, cand_list) == 0)
1172 continue;
1174 cand_class = VTABLE_get_pmc_keyed_int(interp, cand_list, 0);
1175 cand_count++;
1177 for (j = 0; j < list_count; j++) {
1178 /* Skip the current list. */
1179 if (j != i) {
1180 /* Is it in the tail? If so, reject. */
1181 PMC * const check_list =
1182 VTABLE_get_pmc_keyed_int(interp, merge_list, j);
1184 const int check_length = VTABLE_elements(interp, check_list);
1185 int k;
1187 for (k = 1; k < check_length; k++) {
1188 if (VTABLE_get_pmc_keyed_int(interp, check_list, k) ==
1189 cand_class) {
1190 reject = 1;
1191 break;
1197 /* If we didn't reject it, this candidate will do. */
1198 if (!reject) {
1199 accepted = cand_class;
1200 break;
1204 /* If we never found any candidates, return an empty list. */
1205 if (cand_count == 0)
1206 return pmc_new(interp, enum_class_ResizablePMCArray);
1208 /* If we didn't find anything to accept, error. */
1209 if (PMC_IS_NULL(accepted))
1210 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
1211 "Could not build C3 linearization: ambiguous hierarchy");
1213 /* Otherwise, remove what was accepted from the merge lists. */
1214 for (i = 0; i < list_count; i++) {
1215 int j;
1217 PMC * const list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1218 const int list_count = VTABLE_elements(interp, list);
1220 for (j = 0; j < list_count; j++) {
1221 if (VTABLE_get_pmc_keyed_int(interp, list, j) == accepted) {
1222 VTABLE_delete_keyed_int(interp, list, j);
1223 break;
1228 /* Need to merge what remains of the list, then put what was accepted on
1229 * the start of the list, and we're done. */
1230 result = C3_merge(interp, merge_list);
1231 VTABLE_unshift_pmc(interp, result, accepted);
1233 return result;
1239 =item C<PMC* Parrot_ComputeMRO_C3>
1241 Computes the C3 linearization for the given class.
1243 =cut
1247 PARROT_API
1248 PARROT_WARN_UNUSED_RESULT
1249 PARROT_CAN_RETURN_NULL
1250 PMC*
1251 Parrot_ComputeMRO_C3(PARROT_INTERP, ARGIN(PMC *_class))
1253 PMC *result;
1254 PMC * const merge_list = pmc_new(interp, enum_class_ResizablePMCArray);
1255 PMC *immediate_parents;
1256 int i, parent_count;
1258 /* Now get immediate parents list. */
1259 Parrot_PCCINVOKE(interp, _class, CONST_STRING(interp, "parents"),
1260 "->P", &immediate_parents);
1262 if (!immediate_parents)
1263 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_METH_NOT_FOUND,
1264 "Failed to get parents list from class!");
1266 parent_count = VTABLE_elements(interp, immediate_parents);
1268 if (parent_count == 0) {
1269 /* No parents - MRO just contains this class. */
1270 result = pmc_new(interp, enum_class_ResizablePMCArray);
1271 VTABLE_push_pmc(interp, result, _class);
1272 return result;
1275 /* Otherwise, need to do merge. For that, need linearizations of all of
1276 * our parents added to the merge list. */
1277 for (i = 0; i < parent_count; i++) {
1278 PMC * const lin = Parrot_ComputeMRO_C3(interp,
1279 VTABLE_get_pmc_keyed_int(interp, immediate_parents, i));
1281 if (PMC_IS_NULL(lin))
1282 return PMCNULL;
1284 VTABLE_push_pmc(interp, merge_list, lin);
1287 /* Finally, need list of direct parents on the end of the merge list, then
1288 * we can merge. */
1289 VTABLE_push_pmc(interp, merge_list, immediate_parents);
1290 result = C3_merge(interp, merge_list);
1292 if (PMC_IS_NULL(result))
1293 return PMCNULL;
1295 /* Merged result needs this class on the start, and then we're done. */
1296 VTABLE_unshift_pmc(interp, result, _class);
1298 return result;
1304 =item C<void Parrot_ComposeRole>
1306 Used by the Class and Object PMCs internally to compose a role into either of
1307 them. The C<role> parameter is the role that we are composing into the class
1308 or role. C<methods_hash> is the hash of method names to invokable PMCs that
1309 contains the methods the class or role has. C<roles_list> is the list of roles
1310 the the class or method does.
1312 The C<role> parameter is only dealt with by its external interface. Whether
1313 this routine is usable by any other object system implemented in Parrot very
1314 much depends on how closely the role composition semantics they want are to
1315 the default implementation.
1317 =cut
1321 PARROT_API
1322 void
1323 Parrot_ComposeRole(PARROT_INTERP, ARGIN(PMC *role),
1324 ARGIN(PMC *exclude), int got_exclude,
1325 ARGIN(PMC *alias), int got_alias,
1326 ARGIN(PMC *methods_hash), ARGIN(PMC *roles_list))
1328 PMC *methods;
1329 PMC *methods_iter;
1330 PMC *roles_of_role;
1331 PMC *proposed_add_methods;
1333 int i, roles_of_role_count;
1335 /* Check we have not already composed the role; if so, just ignore it. */
1336 int roles_count = VTABLE_elements(interp, roles_list);
1338 for (i = 0; i < roles_count; i++)
1339 if (VTABLE_get_pmc_keyed_int(interp, roles_list, i) == role)
1340 return;
1342 /* Get the methods from the role. */
1343 Parrot_PCCINVOKE(interp, role,
1344 CONST_STRING(interp, "methods"), "->P", &methods);
1346 if (PMC_IS_NULL(methods))
1347 return;
1349 /* We need to check for conflicts before we do the composition. We
1350 * put each method that would be OK to add into a proposal list, and
1351 * bail out right away if we find a problem. */
1352 proposed_add_methods = pmc_new(interp, enum_class_Hash);
1353 methods_iter = VTABLE_get_iter(interp, methods);
1355 while (VTABLE_get_bool(interp, methods_iter)) {
1356 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1357 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp, methods,
1358 method_name);
1360 /* Need to find the name we'll check for a conflict on. */
1361 int excluded = 0;
1363 /* Check if it's in the exclude list. */
1364 if (got_exclude) {
1365 const int exclude_count = VTABLE_elements(interp, exclude);
1367 for (i = 0; i < exclude_count; i++) {
1368 const STRING * const check =
1369 VTABLE_get_string_keyed_int(interp, exclude, i);
1371 if (string_equal(interp, check, method_name) == 0) {
1372 excluded = 1;
1373 break;
1378 /* If we weren't excluded... */
1379 if (!excluded) {
1380 /* Is there a method with this name already in the class?
1381 * RT#45999 multi-method handling. */
1383 if (VTABLE_exists_keyed_str(interp, methods_hash, method_name))
1384 /* Conflicts with something already in the class. */
1385 Parrot_ex_throw_from_c_args(interp, NULL,
1386 EXCEPTION_ROLE_COMPOSITION_METH_CONFLICT,
1387 "A conflict occurred during role composition "
1388 "due to method '%S'.", method_name);
1390 /* What about a conflict with ourslef? */
1391 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1392 method_name))
1393 /* Something very weird is going on. */
1394 Parrot_ex_throw_from_c_args(interp, NULL,
1395 EXCEPTION_ROLE_COMPOSITION_METH_CONFLICT,
1396 "A conflict occurred during role composition;"
1397 " the method '%S' from the role managed to conflict "
1398 "with itself somehow.", method_name);
1400 /* If we got here, no conflicts! Add method to the "to compose"
1401 * list. */
1402 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1403 method_name, cur_method);
1406 /* Now see if we've got an alias. */
1407 if (got_alias && VTABLE_exists_keyed_str(interp, alias, method_name)) {
1408 /* Got one. Get name to alias it to. */
1409 STRING * const alias_name = VTABLE_get_string_keyed_str(interp,
1410 alias, method_name);
1412 /* Is there a method with this name already in the class?
1413 * RT#45999: multi-method handling. */
1414 if (VTABLE_exists_keyed_str(interp, methods_hash, alias_name))
1415 /* Conflicts with something already in the class. */
1416 Parrot_ex_throw_from_c_args(interp, NULL,
1417 EXCEPTION_ROLE_COMPOSITION_METH_CONFLICT,
1418 "A conflict occurred during role composition"
1419 " due to the aliasing of '%S' to '%S'.",
1420 method_name, alias_name);
1422 /* What about a conflict with ourslef? */
1423 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1424 alias_name))
1425 Parrot_ex_throw_from_c_args(interp, NULL,
1426 EXCEPTION_ROLE_COMPOSITION_METH_CONFLICT,
1427 "A conflict occurred during role composition"
1428 " due to the aliasing of '%S' to '%S' (role already has"
1429 " a method '%S').", method_name, alias_name, alias_name);
1431 /* If we get here, no conflicts! Add method to the "to compose"
1432 * list with its alias. */
1433 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1434 alias_name, cur_method);
1438 /* If we get here, we detected no conflicts. Go ahead and compose the
1439 * methods. */
1440 methods_iter = VTABLE_get_iter(interp, proposed_add_methods);
1442 while (VTABLE_get_bool(interp, methods_iter)) {
1443 /* Get current method and its name. */
1444 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1445 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp,
1446 proposed_add_methods, method_name);
1448 /* Add it to the methods of the class. */
1449 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1452 /* Add this role to the roles list. */
1453 VTABLE_push_pmc(interp, roles_list, role);
1454 roles_count++;
1456 /* As a result of composing this role, we will also now do the roles
1457 * that it did itself. Note that we already have the correct methods
1458 * as roles "flatten" the methods they get from other roles into their
1459 * own method list. */
1460 Parrot_PCCINVOKE(interp, role,
1461 CONST_STRING(interp, "roles"), "->P", &roles_of_role);
1462 roles_of_role_count = VTABLE_elements(interp, roles_of_role);
1464 for (i = 0; i < roles_of_role_count; i++) {
1465 /* Only add if we don't already have it in the list. */
1466 PMC * const cur_role = VTABLE_get_pmc_keyed_int(interp,
1467 roles_of_role, i);
1468 int j;
1470 for (j = 0; j < roles_count; j++) {
1471 if (VTABLE_get_pmc_keyed_int(interp, roles_list, j) == cur_role) {
1472 /* We ain't be havin' it. */
1473 VTABLE_push_pmc(interp, roles_list, cur_role);
1482 =back
1484 =head1 SEE ALSO
1486 F<include/parrot/oo.h>, F<include/parrot/oo_private.h>,
1487 F<docs/pdds/pdd15_objects.pod>.
1489 =cut
1494 * Local variables:
1495 * c-file-style: "parrot"
1496 * End:
1497 * vim: expandtab shiftwidth=4: