* t/pmc/complex.t:
[parrot.git] / src / oo.c
blob87ad117eedd4c058a8517f033f9ce5c9dc9352ac
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 */
33 PARROT_WARN_UNUSED_RESULT
34 PARROT_CAN_RETURN_NULL
35 static PMC* C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
36 __attribute__nonnull__(1)
37 __attribute__nonnull__(2);
39 static void create_deleg_pmc_vtable(PARROT_INTERP,
40 ARGIN(PMC *_class),
41 int full)
42 __attribute__nonnull__(1)
43 __attribute__nonnull__(2);
45 static void debug_trace_find_meth(PARROT_INTERP,
46 ARGIN(const PMC *_class),
47 ARGIN(const STRING *name),
48 ARGIN_NULLOK(const PMC *sub))
49 __attribute__nonnull__(1)
50 __attribute__nonnull__(2)
51 __attribute__nonnull__(3);
53 static void do_initcall(PARROT_INTERP,
54 ARGIN_NULLOK(PMC* _class),
55 ARGIN_NULLOK(PMC *object),
56 ARGIN_NULLOK(PMC *init))
57 __attribute__nonnull__(1);
59 static void fail_if_type_exists(PARROT_INTERP, ARGIN(PMC *name))
60 __attribute__nonnull__(1)
61 __attribute__nonnull__(2);
63 PARROT_WARN_UNUSED_RESULT
64 PARROT_CAN_RETURN_NULL
65 static PMC * find_method_direct_1(PARROT_INTERP,
66 ARGIN(PMC *_class),
67 ARGIN(STRING *method_name))
68 __attribute__nonnull__(1)
69 __attribute__nonnull__(2)
70 __attribute__nonnull__(3);
72 PARROT_WARN_UNUSED_RESULT
73 PARROT_CAN_RETURN_NULL
74 static PMC* find_vtable_meth_ns(PARROT_INTERP,
75 ARGIN(PMC *ns),
76 INTVAL vtable_index)
77 __attribute__nonnull__(1)
78 __attribute__nonnull__(2);
80 PARROT_WARN_UNUSED_RESULT
81 PARROT_CAN_RETURN_NULL
82 static PMC* get_init_meth(PARROT_INTERP,
83 ARGIN(PMC *_class),
84 ARGIN(STRING *prop_str),
85 ARGOUT(STRING **meth_str))
86 __attribute__nonnull__(1)
87 __attribute__nonnull__(2)
88 __attribute__nonnull__(3)
89 __attribute__nonnull__(4)
90 FUNC_MODIFIES(*meth_str);
92 static void instantiate_object(PARROT_INTERP,
93 ARGMOD(PMC *object),
94 ARGIN_NULLOK(PMC *init))
95 __attribute__nonnull__(1)
96 __attribute__nonnull__(2)
97 FUNC_MODIFIES(*object);
99 static void invalidate_all_caches(PARROT_INTERP)
100 __attribute__nonnull__(1);
102 static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)
103 __attribute__nonnull__(1);
105 static void parrot_class_register(PARROT_INTERP,
106 ARGIN(PMC *name),
107 ARGIN(PMC *new_class),
108 ARGIN_NULLOK(PMC *parent),
109 ARGIN(PMC *mro))
110 __attribute__nonnull__(1)
111 __attribute__nonnull__(2)
112 __attribute__nonnull__(3)
113 __attribute__nonnull__(5);
115 static void rebuild_attrib_stuff(PARROT_INTERP, ARGIN(PMC *_class))
116 __attribute__nonnull__(1)
117 __attribute__nonnull__(2);
119 /* HEADERIZER END: static */
123 =item C<void Parrot_oo_extract_methods_from_namespace>
125 Extract methods and vtable overrides from the given namespace and insert them
126 into the class.
128 =cut
132 void
133 Parrot_oo_extract_methods_from_namespace(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *ns))
135 PMC *methods, *vtable_overrides;
137 /* Pull in methods from the namespace, if any. */
138 if (PMC_IS_NULL(ns))
139 return;
141 /* Import any methods. */
142 Parrot_PCCINVOKE(interp, ns,
143 CONST_STRING(interp, "get_associated_methods"), "->P", &methods);
145 if (!PMC_IS_NULL(methods)) {
146 PMC * const iter = VTABLE_get_iter(interp, methods);
148 while (VTABLE_get_bool(interp, iter)) {
149 STRING * const meth_name = VTABLE_shift_string(interp, iter);
150 PMC * const meth_sub = VTABLE_get_pmc_keyed_str(interp, methods,
151 meth_name);
152 VTABLE_add_method(interp, self, meth_name, meth_sub);
156 /* Import any vtable methods. */
157 Parrot_PCCINVOKE(interp, ns,
158 CONST_STRING(interp, "get_associated_vtable_methods"), "->P", &vtable_overrides);
160 if (!PMC_IS_NULL(vtable_overrides)) {
161 PMC * const iter = VTABLE_get_iter(interp, vtable_overrides);
162 while (VTABLE_get_bool(interp, iter)) {
163 STRING * const vtable_index_str = VTABLE_shift_string(interp, iter);
164 PMC * const vtable_sub = VTABLE_get_pmc_keyed_str(interp,
165 vtable_overrides, vtable_index_str);
167 /* Look up the name of the vtable function from the index. */
168 const INTVAL vtable_index = string_to_int(interp, vtable_index_str);
169 const char * const meth_c = Parrot_vtable_slot_names[vtable_index];
170 STRING *vtable_name = string_from_cstring(interp, meth_c, 0);
172 /* Strip leading underscores in the vtable name */
173 if (string_str_index(interp, vtable_name,
174 CONST_STRING(interp, "__"), 0) == 0) {
175 vtable_name = string_substr(interp, vtable_name, 2,
176 string_length(interp, vtable_name) - 2, NULL, 0);
179 VTABLE_add_vtable_override(interp, self, vtable_name, vtable_sub);
187 =item C<PMC * Parrot_oo_get_namespace>
189 Lookup a namespace object from a class PMC.
191 =cut
195 PARROT_CAN_RETURN_NULL
196 PARROT_WARN_UNUSED_RESULT
197 PMC *
198 Parrot_oo_get_namespace(SHIM_INTERP, ARGIN(const PMC *classobj))
200 Parrot_Class * const _class = PARROT_CLASS(classobj);
201 PMC * const _namespace = _class->_namespace;
203 if (PMC_IS_NULL(_namespace))
204 return PMCNULL;
206 return _namespace;
212 =item C<PMC * Parrot_oo_get_class>
214 Lookup a class object from a namespace, string, or key PMC.
216 =cut
220 PARROT_API
221 PARROT_CAN_RETURN_NULL
222 PARROT_WARN_UNUSED_RESULT
223 PMC *
224 Parrot_oo_get_class(PARROT_INTERP, ARGIN(PMC *key))
226 PMC *classobj = PMCNULL;
228 if (PObj_is_class_TEST(key))
229 classobj = key;
230 else {
231 /* Fast select of behavior based on type of the lookup key */
232 switch (key->vtable->base_type) {
233 case enum_class_NameSpace:
234 classobj = VTABLE_get_class(interp, key);
235 break;
237 case enum_class_String:
238 case enum_class_Key:
239 case enum_class_ResizableStringArray:
241 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp,
242 interp->HLL_namespace,
243 CONTEXT(interp)->current_HLL);
244 PMC * const ns = Parrot_get_namespace_keyed(interp,
245 hll_ns, key);
247 if (!PMC_IS_NULL(ns))
248 classobj = VTABLE_get_class(interp, ns);
250 default:
251 break;
255 if (PMC_IS_NULL(classobj)) {
256 /* Look up a low-level class and create a proxy */
257 const INTVAL type = pmc_type(interp, VTABLE_get_string(interp, key));
259 /* Reject invalid type numbers */
260 if (type > interp->n_vtable_max || type <= 0)
261 return PMCNULL;
262 else {
263 PMC * const type_num = pmc_new(interp, enum_class_Integer);
264 VTABLE_set_integer_native(interp, type_num, type);
265 return pmc_new_init(interp, enum_class_PMCProxy, type_num);
269 return classobj;
275 =item C<PMC * Parrot_oo_get_class_str>
277 Lookup a class object from a builtin string.
279 =cut
283 PARROT_API
284 PARROT_CAN_RETURN_NULL
285 PARROT_WARN_UNUSED_RESULT
286 PMC *
287 Parrot_oo_get_class_str(PARROT_INTERP, ARGIN(STRING *name))
289 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp, interp->HLL_namespace,
290 CONTEXT(interp)->current_HLL);
291 PMC * const ns = Parrot_get_namespace_keyed_str(interp, hll_ns, name);
292 PMC * const _class = PMC_IS_NULL(ns)
293 ? PMCNULL : VTABLE_get_class(interp, ns);
295 /* Look up a low-level class and create a proxy */
296 if (PMC_IS_NULL(_class)) {
297 const INTVAL type = pmc_type(interp, name);
299 /* Reject invalid type numbers */
300 if (type > interp->n_vtable_max || type <= 0)
301 return PMCNULL;
302 else {
303 PMC * const type_num = pmc_new(interp, enum_class_Integer);
304 VTABLE_set_integer_native(interp, type_num, type);
305 return pmc_new_init(interp, enum_class_PMCProxy, type_num);
309 return _class;
315 =item C<PMC * Parrot_oo_newclass_from_str>
317 Create a new class object from a string name.
319 =cut
323 PARROT_CAN_RETURN_NULL
324 PARROT_WARN_UNUSED_RESULT
325 PMC *
326 Parrot_oo_newclass_from_str(PARROT_INTERP, ARGIN(STRING *name))
328 PMC * const namearg = pmc_new(interp, enum_class_String);
329 PMC *namehash = pmc_new(interp, enum_class_Hash);
330 PMC *classobj;
332 VTABLE_set_string_native(interp, namearg, name);
333 VTABLE_set_pmc_keyed_str(interp, namehash, CONST_STRING(interp, "name"), namearg);
335 classobj = pmc_new_init(interp, enum_class_Class, namehash);
337 PARROT_ASSERT(classobj);
338 return classobj;
344 =item C<PMC * Parrot_oo_find_vtable_override_for_class>
346 Lookup a vtable override in a specific class object.
348 =cut
352 PARROT_CAN_RETURN_NULL
353 PARROT_WARN_UNUSED_RESULT
354 PMC *
355 Parrot_oo_find_vtable_override_for_class(PARROT_INTERP,
356 ARGIN(PMC *classobj), ARGIN(STRING *name))
358 Parrot_Class *class_info;
359 PARROT_ASSERT(PObj_is_class_TEST(classobj));
361 class_info = PARROT_CLASS(classobj);
362 return VTABLE_get_pmc_keyed_str(interp, class_info->vtable_overrides, name);
368 =item C<PMC * Parrot_oo_find_vtable_override>
370 Lookup a vtable override in a class, including any vtable overrides inherited
371 from parents.
373 =cut
377 PARROT_CAN_RETURN_NULL
378 PARROT_WARN_UNUSED_RESULT
379 PMC *
380 Parrot_oo_find_vtable_override(PARROT_INTERP,
381 ARGIN(PMC *classobj), ARGIN(STRING *name))
383 Parrot_Class * const _class = PARROT_CLASS(classobj);
385 /* Walk and search for the vtable method. */
386 const INTVAL num_classes = VTABLE_elements(interp, _class->all_parents);
387 INTVAL i;
389 for (i = 0; i < num_classes; i++) {
390 /* Get the class. */
391 PMC * const cur_class =
392 VTABLE_get_pmc_keyed_int(interp, _class->all_parents, i);
394 PMC * const meth =
395 Parrot_oo_find_vtable_override_for_class(interp, cur_class, name);
397 if (!PMC_IS_NULL(meth))
398 return meth;
401 return PMCNULL;
407 =item C<INTVAL Parrot_get_vtable_index>
409 Return index if C<name> is a valid vtable slot name.
411 =cut
415 PARROT_API
416 INTVAL
417 Parrot_get_vtable_index(PARROT_INTERP, ARGIN(const STRING *name))
419 char * const name_c = string_to_cstring(interp, name);
421 /* some of the first "slots" don't have names. skip 'em. */
422 INTVAL low = PARROT_VTABLE_LOW;
423 INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW;
425 while (low < high) {
426 const INTVAL mid = (low + high) / 2;
427 const char * const meth_c = Parrot_vtable_slot_names[mid];
429 /* RT#45965 slot_names still have __ in front */
430 const INTVAL cmp = strcmp(name_c, meth_c + 2);
432 if (cmp == 0) {
433 string_cstring_free(name_c);
434 return mid;
436 else if (cmp > 0)
437 low = mid + 1;
438 else
439 high = mid;
442 string_cstring_free(name_c);
444 return -1;
450 =item C<static PMC* find_vtable_meth_ns>
452 Return Sub PMC if a method with the vtable name exists in ns
454 =cut
458 PARROT_WARN_UNUSED_RESULT
459 PARROT_CAN_RETURN_NULL
460 static PMC*
461 find_vtable_meth_ns(PARROT_INTERP, ARGIN(PMC *ns), INTVAL vtable_index)
463 return VTABLE_get_pmc_keyed_int(interp, ns, vtable_index);
469 =item C<PMC* Parrot_find_vtable_meth>
471 Given pmc, run through its mro looking for the meth vtable method.
472 Return the vtable method PMC if found.
474 =cut
478 PARROT_API
479 PARROT_CAN_RETURN_NULL
480 PMC*
481 Parrot_find_vtable_meth(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(STRING *meth))
483 INTVAL i, n;
484 PMC *mro;
485 PMC *_class = pmc;
487 /* Get index in Parrot_vtable_slot_names[]. */
488 const INTVAL vtable_index = Parrot_get_vtable_index(interp, meth);
490 if (vtable_index == -1)
491 return PMCNULL;
493 /* Get class. */
494 if (PObj_is_object_TEST(pmc))
495 _class = GET_CLASS(pmc);
497 /* Get MRO and iterate over it to find method with a matching
498 vtable index or double-underscored name. */
499 mro = _class->vtable->mro;
500 n = VTABLE_elements(interp, mro);
502 for (i = 0; i < n; ++i) {
503 PMC *ns = VTABLE_get_namespace(interp, _class);
504 PMC *_class = VTABLE_get_pmc_keyed_int(interp, mro, i);
506 if (!PMC_IS_NULL(ns)) {
507 PMC * const res = find_vtable_meth_ns(interp, ns, vtable_index);
509 if (!PMC_IS_NULL(res))
510 return res;
514 /* If we get here, method is not overridden in the class. */
515 return PMCNULL;
521 =item C<STRING* readable_name>
523 Given a String or Key PMC return the STRING* representation
525 RT#45967 this function, key_set_to_string, and the key PMC get_repr should be
526 consolidated
528 =cut
532 PARROT_API
533 PARROT_WARN_UNUSED_RESULT
534 PARROT_CANNOT_RETURN_NULL
535 STRING*
536 readable_name(PARROT_INTERP, ARGIN(PMC *name))
538 PMC *array;
540 if (name->vtable->base_type == enum_class_String)
541 return VTABLE_get_string(interp, name);
543 array = pmc_new(interp, enum_class_ResizableStringArray);
545 PARROT_ASSERT(name->vtable->base_type == enum_class_Key);
547 while (name) {
548 VTABLE_push_string(interp, array, key_string(interp, name));
549 name = key_next(interp, name);
552 return string_join(interp, CONST_STRING(interp, ";"), array);
558 =item C<static void rebuild_attrib_stuff>
560 Take the class and completely rebuild the attribute stuff for
561 it. Horribly destructive, and definitely not a good thing to do if
562 there are instantiated objects for the class
564 =cut
568 static void
569 rebuild_attrib_stuff(PARROT_INTERP, ARGIN(PMC *_class))
571 INTVAL attr_count, cur_offset, n_class, n_mro, offset;
572 PMC *attr_offset_hash, *mro, *attribs;
573 SLOTTYPE *class_slots;
575 #ifndef NDEBUG
576 PMC * const orig_class = _class;
577 #endif
579 /* attrib count isn't set yet, a GC caused by concat could
580 * corrupt data under construction */
581 Parrot_block_DOD(interp);
583 class_slots = PMC_data_typed(_class, SLOTTYPE *);
584 attr_offset_hash = pmc_new(interp, enum_class_Hash);
585 set_attrib_num(_class, class_slots, PCD_ATTRIBUTES, attr_offset_hash);
587 mro = _class->vtable->mro;
588 n_mro = VTABLE_elements(interp, mro);
590 /* walk from oldest parent down to n_class == 0 which is this class */
591 cur_offset = 0;
593 for (n_class = n_mro - 1; n_class >= 0; --n_class) {
594 STRING *classname;
596 _class = VTABLE_get_pmc_keyed_int(interp, mro, n_class);
598 /* this Class isa PMC - no attributes there */
599 if (!PObj_is_class_TEST(_class))
600 continue;
602 class_slots = PMC_data_typed(_class, SLOTTYPE *);
603 classname = VTABLE_get_string(interp,
604 get_attrib_num(class_slots, PCD_CLASS_NAME));
605 attribs = get_attrib_num(class_slots, PCD_CLASS_ATTRIBUTES);
606 attr_count = VTABLE_elements(interp, attribs);
608 if (attr_count) {
609 STRING * const partial_name = string_concat(interp, classname,
610 CONST_STRING(interp, "\0"), 0);
612 for (offset = 0; offset < attr_count; offset++) {
613 STRING * const attr_name =
614 VTABLE_get_string_keyed_int(interp, attribs, offset);
615 STRING * const full_name =
616 string_concat(interp, partial_name, attr_name, 0);
618 /* store this attribute with short and full name */
620 VTABLE_set_integer_keyed_str(interp, attr_offset_hash,
621 attr_name, cur_offset);
622 VTABLE_set_integer_keyed_str(interp, attr_offset_hash,
623 full_name, cur_offset);
624 cur_offset++;
629 #ifndef NDEBUG
630 PARROT_ASSERT(_class == orig_class);
631 #endif
633 /* And note the totals */
634 CLASS_ATTRIB_COUNT(_class) = cur_offset;
635 Parrot_unblock_DOD(interp);
641 =item C<static void create_deleg_pmc_vtable>
643 Create a vtable that dispatches either to the contained PMC in the first
644 attribute (deleg_pmc) or to an overridden method (delegate), depending
645 on the existence of the method for this class.
647 =cut
651 static void
652 create_deleg_pmc_vtable(PARROT_INTERP, ARGIN(PMC *_class), int full)
654 int i;
655 const char *meth;
656 STRING meth_str;
657 DECL_CONST_CAST;
659 PMC * const vtable_pmc = get_attrib_num(PMC_data_typed(_class,
660 SLOTTYPE *), PCD_OBJECT_VTABLE);
661 VTABLE * const vtable = (VTABLE *)PMC_struct_val(vtable_pmc);
662 VTABLE * const ro_vtable = vtable->ro_variant_vtable;
663 VTABLE * const deleg_pmc_vtable = interp->vtables[enum_class_deleg_pmc];
664 VTABLE * const object_vtable = interp->vtables[enum_class_Object];
665 VTABLE * const ro_object_vtable = object_vtable->ro_variant_vtable;
666 VTABLE * const delegate_vtable = interp->vtables[enum_class_delegate];
668 memset(&meth_str, 0, sizeof (meth_str));
670 meth_str.encoding = Parrot_fixed_8_encoding_ptr;
671 meth_str.charset = Parrot_default_charset_ptr;
673 for (i = 0; (meth = Parrot_vtable_slot_names[i]) != NULL; ++i) {
674 if (!*meth)
675 continue;
677 /* strip underscores from method name */
678 meth_str.strstart = (char *)const_cast(meth + 2);
679 meth_str.strlen = meth_str.bufused = strlen(meth) - 2;
680 meth_str.hashval = 0;
682 if (!PMC_IS_NULL(Parrot_find_vtable_meth(interp, _class, &meth_str))) {
683 /* the method exists; keep the ParrotObject delegate vtable slot */
684 ((void **)vtable)[i] = ((void**)object_vtable)[i];
685 if (ro_vtable)
686 ((void **)ro_vtable)[i] = ((void**)ro_object_vtable)[i];
688 else if (full) {
690 * the method doesn't exist; put in the deleg_pmc vtable,
691 * but only if ParrotObject hasn't overridden the method
693 if (((void **)delegate_vtable)[i] == ((void**)object_vtable)[i]) {
694 if (ro_vtable)
695 ((void **)ro_vtable)[i] = ((void**)deleg_pmc_vtable)[i];
696 ((void **)vtable)[i] = ((void**)deleg_pmc_vtable)[i];
698 else {
699 ((void **)vtable)[i] = ((void**)object_vtable)[i];
700 if (ro_vtable)
701 ((void **)ro_vtable)[i] = ((void**)ro_object_vtable)[i];
711 =item C<const char* Parrot_MMD_method_name>
713 Return the method name for the given MMD enum.
715 =cut
719 PARROT_API
720 PARROT_PURE_FUNCTION
721 PARROT_CAN_RETURN_NULL
722 const char*
723 Parrot_MMD_method_name(SHIM_INTERP, INTVAL idx)
725 PARROT_ASSERT(idx >= 0);
727 if (idx >= MMD_USER_FIRST)
728 return NULL;
730 return Parrot_mmd_func_names[idx];
736 =item C<INTVAL Parrot_MMD_method_idx>
738 Return the MMD function number for method name or -1 on failure.
740 RT#45973 allow dynamic expansion at runtime.
742 =cut
746 PARROT_API
747 PARROT_PURE_FUNCTION
748 INTVAL
749 Parrot_MMD_method_idx(SHIM_INTERP, ARGIN(const char *name))
751 INTVAL i;
753 for (i = 0; i < MMD_USER_FIRST; ++i) {
754 if (STREQ(Parrot_mmd_func_names[i], name))
755 return i;
758 return -1;
764 =item C<PMC * Parrot_single_subclass>
766 Subclass a class. Single parent class, nice and straightforward. If
767 C<child_class> is C<NULL>, this is an anonymous subclass we're creating,
768 function.
770 =cut
774 PARROT_API
775 PARROT_WARN_UNUSED_RESULT
776 PARROT_CANNOT_RETURN_NULL
777 PMC *
778 Parrot_single_subclass(PARROT_INTERP, ARGIN(PMC *base_class), ARGIN_NULLOK(PMC *name))
780 PMC *child_class, *parents, *temp_pmc, *mro;
781 SLOTTYPE *child_class_array;
782 int parent_is_class;
784 /* Set the classname, if we have one */
785 if (!PMC_IS_NULL(name)) {
786 fail_if_type_exists(interp, name);
788 else {
789 /* RT#45975 not really threadsafe but good enough for now */
790 static int anon_count;
791 STRING * const child_class_name =
792 Parrot_sprintf_c(interp, "%c%canon_%d", 0, 0, ++anon_count);
793 name = pmc_new(interp, enum_class_String);
794 VTABLE_set_string_native(interp, name, child_class_name);
797 /* ParrotClass is the baseclass anyway, so build just a new class */
798 if (base_class == interp->vtables[enum_class_Class]->pmc_class)
799 return pmc_new_init(interp, enum_class_Class, name);
801 parent_is_class = PObj_is_class_TEST(base_class);
802 child_class = pmc_new(interp, enum_class_Class);
804 /* Hang an array off the data pointer */
805 set_attrib_array_size(child_class, PCD_MAX);
806 child_class_array = PMC_data_typed(child_class, SLOTTYPE *);
807 set_attrib_flags(child_class);
809 /* We will have five entries in this array */
811 /* We have the same number of attributes as our parent */
812 CLASS_ATTRIB_COUNT(child_class) = parent_is_class ?
813 CLASS_ATTRIB_COUNT(base_class) : 0;
815 /* Our parent class array has a single member in it */
816 parents = pmc_new(interp, enum_class_ResizablePMCArray);
818 VTABLE_set_integer_native(interp, parents, 1);
819 VTABLE_set_pmc_keyed_int(interp, parents, 0, base_class);
821 set_attrib_num(child_class, child_class_array, PCD_PARENTS, parents);
822 set_attrib_num(child_class, child_class_array, PCD_CLASS_NAME, name);
824 /* Our mro list is a clone of our parent's mro list,
825 * with our self unshifted onto the beginning */
826 mro = VTABLE_clone(interp, base_class->vtable->mro);
827 VTABLE_unshift_pmc(interp, mro, child_class);
829 /* But we have no attributes of our own. Yet */
830 temp_pmc = pmc_new(interp, enum_class_ResizablePMCArray);
831 set_attrib_num(child_class, child_class_array, PCD_CLASS_ATTRIBUTES,
832 temp_pmc);
834 parrot_class_register(interp, name, child_class, base_class, mro);
835 rebuild_attrib_stuff(interp, child_class);
837 if (!parent_is_class) {
838 /* we append one attribute to hold the PMC */
839 Parrot_add_attribute(interp, child_class,
840 CONST_STRING(interp, "__value"));
842 * then create a vtable derived from ParrotObject and
843 * deleg_pmc - the ParrotObject vtable is already built
845 create_deleg_pmc_vtable(interp, child_class, 1);
847 else {
849 * if any parent isa PMC, then still individual vtables might
850 * be overridden in this subclass
852 int i;
853 int any_pmc_parent = 0;
854 const int n = VTABLE_elements(interp, mro);
856 /* 0 = this, 1 = parent (handled above), 2 = grandpa */
857 for (i = 2; i < n; ++i) {
858 const PMC * const parent = VTABLE_get_pmc_keyed_int(interp, mro, i);
859 if (!PObj_is_class_TEST(parent)) {
860 any_pmc_parent = 1;
861 break;
864 if (any_pmc_parent)
865 create_deleg_pmc_vtable(interp, child_class, 0);
868 return child_class;
874 =item C<PMC * Parrot_class_lookup>
876 Looks for the class named C<class_name> and returns it if it exists.
877 Otherwise it returns C<PMCNULL>.
879 =cut
883 PARROT_API
884 PARROT_CAN_RETURN_NULL
885 PARROT_WARN_UNUSED_RESULT
886 PMC *
887 Parrot_class_lookup(PARROT_INTERP, ARGIN(STRING *class_name))
889 const INTVAL type = pmc_type(interp, class_name);
890 PMC *pmc;
892 if (type <= 0)
893 return PMCNULL;
895 pmc = interp->vtables[type]->pmc_class;
896 PARROT_ASSERT(pmc);
897 return pmc;
903 =item C<PMC * Parrot_class_lookup_p>
905 Looks for the class named C<class_name> and returns it if it exists.
906 Otherwise it returns C<PMCNULL>.
908 =cut
912 PARROT_CAN_RETURN_NULL
913 PARROT_WARN_UNUSED_RESULT
914 PMC *
915 Parrot_class_lookup_p(PARROT_INTERP, ARGIN(PMC *class_name))
917 const INTVAL type = pmc_type_p(interp, class_name);
918 PMC *pmc;
920 if (type <= 0)
921 return PMCNULL;
923 pmc = interp->vtables[type]->pmc_class;
924 PARROT_ASSERT(pmc);
925 return pmc;
931 =item C<static void fail_if_type_exists>
933 This function throws an exception if a PMC or class with the same name *
934 already exists in the global type registry. The global type registry
935 will go away eventually, but this allows the new object metamodel to
936 interact with the old one until it does.
938 =cut
942 static void
943 fail_if_type_exists(PARROT_INTERP, ARGIN(PMC *name))
945 INTVAL type;
947 PMC * const classname_hash = interp->class_hash;
948 PMC * const type_pmc = (PMC *)VTABLE_get_pointer_keyed(interp,
949 classname_hash, name);
951 if (PMC_IS_NULL(type_pmc)
952 || type_pmc->vtable->base_type == enum_class_NameSpace)
953 type = 0;
954 else
955 type = VTABLE_get_integer(interp, type_pmc);
957 if (type > enum_type_undef)
958 /* RT#46091 get printable name */
959 real_exception(interp, NULL, INVALID_OPERATION,
960 "Class %Ss already registered!\n",
961 VTABLE_get_string(interp, name));
963 if (type < enum_type_undef)
964 real_exception(interp, NULL, INVALID_OPERATION,
965 "native type with name '%s' already exists - "
966 "can't register Class", data_types[type].name);
972 =item C<INTVAL Parrot_oo_register_type>
974 This function registers a type in the global registry, first checking if it
975 already exists. The global type registry will go away eventually, but this
976 allows the new object metamodel to interact with the old one until it does.
978 =cut
982 PARROT_WARN_UNUSED_RESULT
983 INTVAL
984 Parrot_oo_register_type(PARROT_INTERP, ARGIN(PMC *name))
986 INTVAL type;
987 PMC *classname_hash, *item;
989 fail_if_type_exists(interp, name);
991 /* Type doesn't exist, so go ahead and register it. Lock interpreter so
992 * pt_shared_fixup() can safely do a type lookup. */
993 LOCK_INTERPRETER(interp);
994 classname_hash = interp->class_hash;
996 type = interp->n_vtable_max++;
998 /* Have we overflowed the table? */
999 if (type >= interp->n_vtable_alloced)
1000 parrot_realloc_vtables(interp);
1002 /* set entry in name->type hash */
1003 item = pmc_new(interp, enum_class_Integer);
1004 PMC_int_val(item) = type;
1006 VTABLE_set_pmc_keyed(interp, classname_hash, name, item);
1007 UNLOCK_INTERPRETER(interp);
1009 return type;
1015 =item C<static void parrot_class_register>
1017 This is the way to register a new Parrot class as an instantiable
1018 type. Doing this involves putting it in the class hash, setting its
1019 vtable so that the C<init> method initializes objects of the class rather than
1020 the class itself, and adding it to the interpreter's base type table so
1021 you can create a new C<foo> in PASM like this: C<new Px, foo>.
1023 =cut
1027 static void
1028 parrot_class_register(PARROT_INTERP, ARGIN(PMC *name),
1029 ARGIN(PMC *new_class), ARGIN_NULLOK(PMC *parent), ARGIN(PMC *mro))
1031 PMC *vtable_pmc;
1032 const INTVAL new_type = Parrot_oo_register_type(interp, name);
1034 /* check if we already have a NameSpace */
1035 PMC *top = CONTEXT(interp)->current_namespace;
1036 PMC *ns = VTABLE_get_pmc_keyed(interp, top, name);
1038 /* Build a new vtable for this class
1039 * The child class PMC gets the vtable of its parent class or
1040 * a ParrotClass vtable
1042 VTABLE *parent_vtable =
1043 (parent && PObj_is_class_TEST(parent))
1044 ? parent->vtable
1045 : new_class->vtable;
1047 VTABLE *new_vtable = Parrot_clone_vtable(interp, parent_vtable);
1049 /* Set the vtable's type to the newly allocated type */
1050 new_vtable->base_type = new_type;
1052 /* And cache our class PMC in the vtable so we can find it later */
1053 new_vtable->pmc_class = new_class;
1054 new_vtable->mro = mro;
1056 if (parent_vtable->ro_variant_vtable)
1057 new_vtable->ro_variant_vtable =
1058 Parrot_clone_vtable(interp, parent_vtable->ro_variant_vtable);
1060 /* Reset the init method to our instantiation method */
1061 new_vtable->init = Parrot_instantiate_object;
1062 new_vtable->init_pmc = Parrot_instantiate_object_init;
1063 new_class->vtable = new_vtable;
1065 /* Put our new vtable in the global table */
1066 interp->vtables[new_type] = new_vtable;
1068 /* RT#45979 nested, use current as base ? */
1069 if (PMC_IS_NULL(ns)) {
1070 /* RT#45983 try HLL namespace too */
1071 top = Parrot_get_ctx_HLL_namespace(interp);
1072 ns = VTABLE_get_pmc_keyed(interp, top, name);
1075 if (PMC_IS_NULL(ns)) {
1076 ns = pmc_new(interp, enum_class_NameSpace);
1077 VTABLE_set_pmc_keyed(interp, top, name, ns);
1080 /* attach namespace to vtable */
1081 new_vtable->_namespace = ns;
1083 if (new_vtable->ro_variant_vtable) {
1084 VTABLE * const ro_vt = new_vtable->ro_variant_vtable;
1086 ro_vt->base_type = new_vtable->base_type;
1087 ro_vt->pmc_class = new_vtable->pmc_class;
1088 ro_vt->mro = new_vtable->mro;
1089 ro_vt->_namespace = new_vtable->_namespace;
1093 * prepare object vtable - again that of the parent or
1094 * a ParrotObject vtable
1096 if (parent && PObj_is_class_TEST(parent)) {
1097 vtable_pmc =
1098 get_attrib_num((SLOTTYPE *)PMC_data(parent), PCD_OBJECT_VTABLE);
1099 parent_vtable = (VTABLE *)PMC_struct_val(vtable_pmc);
1101 else
1102 parent_vtable = interp->vtables[enum_class_Object];
1104 new_vtable = Parrot_clone_vtable(interp, parent_vtable);
1106 if (parent_vtable->ro_variant_vtable)
1107 new_vtable->ro_variant_vtable =
1108 Parrot_clone_vtable(interp, parent_vtable->ro_variant_vtable);
1110 new_vtable->base_type = new_type;
1111 new_vtable->mro = mro;
1112 new_vtable->pmc_class = new_class;
1114 set_attrib_num(new_class, (SLOTTYPE*)PMC_data(new_class), PCD_OBJECT_VTABLE,
1115 vtable_pmc = constant_pmc_new(interp, enum_class_VtableCache));
1116 PMC_struct_val(vtable_pmc) = new_vtable;
1118 /* attach namespace to object vtable too */
1119 new_vtable->_namespace = ns;
1121 if (new_vtable->ro_variant_vtable) {
1122 VTABLE * const ro_vt = new_vtable->ro_variant_vtable;
1124 ro_vt->base_type = new_vtable->base_type;
1125 ro_vt->pmc_class = new_vtable->pmc_class;
1126 ro_vt->mro = new_vtable->mro;
1127 ro_vt->_namespace = new_vtable->_namespace;
1134 =item C<static PMC* get_init_meth>
1136 RT#48260: Not yet documented!!!
1138 =cut
1142 PARROT_WARN_UNUSED_RESULT
1143 PARROT_CAN_RETURN_NULL
1144 static PMC*
1145 get_init_meth(PARROT_INTERP, ARGIN(PMC *_class),
1146 ARGIN(STRING *prop_str), ARGOUT(STRING **meth_str))
1148 STRING *meth;
1149 HashBucket *b;
1150 PMC *props, *ns, *method;
1152 *meth_str = NULL;
1153 #if 0
1154 PMC * const prop = VTABLE_getprop(interp, _class, prop_str);
1155 if (!VTABLE_defined(interp, prop))
1156 return PMCNULL;
1157 meth = VTABLE_get_string(interp, prop);
1158 #else
1159 props = PMC_metadata(_class);
1160 if (!props)
1161 return PMCNULL;
1163 b = parrot_hash_get_bucket(interp,
1164 (Hash*) PMC_struct_val(props), prop_str);
1165 if (!b)
1166 return PMCNULL;
1168 meth = PMC_str_val((PMC*) b->value);
1169 #endif
1171 *meth_str = meth;
1172 ns = VTABLE_get_namespace(interp, _class);
1173 method = VTABLE_get_pmc_keyed_str(interp, ns, meth);
1175 return method;
1181 =item C<static void do_initcall>
1183 RT#48260: Not yet documented!!!
1185 =cut
1189 static void
1190 do_initcall(PARROT_INTERP, ARGIN_NULLOK(PMC* _class), ARGIN_NULLOK(PMC *object),
1191 ARGIN_NULLOK(PMC *init))
1193 PMC * const classsearch_array = _class->vtable->mro;
1194 INTVAL i, nparents;
1197 * 1) if class has a CONSTRUCT property run it on the object
1198 * no redispatch
1200 * RT#45985 isn't CONSTRUCT for creating new objects?
1202 STRING *meth_str;
1203 PMC *meth = get_init_meth(interp, _class,
1204 CONST_STRING(interp, "CONSTRUCT"), &meth_str);
1205 int default_meth;
1207 if (!PMC_IS_NULL(meth)) {
1208 if (init)
1209 Parrot_run_meth_fromc_args(interp, meth,
1210 object, meth_str, "vP", init);
1211 else
1212 Parrot_run_meth_fromc_args(interp, meth,
1213 object, meth_str, "v");
1216 * 2. if class has a BUILD property call it for all classes
1217 * in reverse search order - this class last.
1219 * Note: mro contains this class as first element
1221 nparents = VTABLE_elements(interp, classsearch_array);
1223 for (i = nparents - 1; i >= 0; --i) {
1224 PMC * const parent_class =
1225 VTABLE_get_pmc_keyed_int(interp, classsearch_array, i);
1227 /* if it's a PMC, we put one PMC of that type into
1228 * the attribute slot #0 and call init() on that PMC */
1229 if (!PObj_is_class_TEST(parent_class)) {
1230 PMC *attr, *next_parent;
1231 SLOTTYPE *obj_data;
1233 /* but only if init isn't inherited
1234 * or rather just on the last non-class parent */
1235 PARROT_ASSERT(i >= 1);
1236 next_parent = VTABLE_get_pmc_keyed_int(interp,
1237 classsearch_array, i - 1);
1239 if (!PObj_is_class_TEST(next_parent))
1240 continue;
1242 attr = pmc_new_noinit(interp, parent_class->vtable->base_type);
1243 obj_data = PMC_data_typed(object, SLOTTYPE *);
1244 set_attrib_num(object, obj_data, 0, attr);
1245 VTABLE_init(interp, attr);
1246 continue;
1249 meth = get_init_meth(interp, parent_class,
1250 CONST_STRING(interp, "BUILD"), &meth_str);
1252 /* no method found and no BUILD property set? */
1253 if (PMC_IS_NULL(meth) && meth_str == NULL) {
1254 PMC *ns;
1255 INTVAL vtable_index;
1257 /* use __init or __init_pmc (depending on if an argument was passed)
1258 * as fallback constructor method, if it exists */
1259 if (init)
1260 meth_str = CONST_STRING(interp, "init_pmc");
1261 else
1262 meth_str = CONST_STRING(interp, "init");
1264 ns = VTABLE_get_namespace(interp, parent_class);
1266 /* can't use find_method, it walks mro */
1267 vtable_index = Parrot_get_vtable_index(interp, meth_str);
1268 meth = find_vtable_meth_ns(interp, ns, vtable_index);
1269 default_meth = 1;
1271 else
1272 default_meth = 0;
1274 if (!PMC_IS_NULL(meth)) {
1275 if (init)
1276 Parrot_run_meth_fromc_args(interp, meth,
1277 object, meth_str, "vP", init);
1278 else
1279 Parrot_run_meth_fromc_args(interp, meth,
1280 object, meth_str, "v");
1282 else if (meth_str != NULL &&
1283 string_length(interp, meth_str) != 0 && !default_meth) {
1284 real_exception(interp, NULL, METH_NOT_FOUND,
1285 "Class BUILD method ('%Ss') not found", meth_str);
1293 =item C<void Parrot_instantiate_object_init>
1295 Creates a Parrot object. Takes a passed-in class PMC that has sufficient
1296 information to describe the layout of the object and makes the object.
1298 =cut
1302 PARROT_API
1303 void
1304 Parrot_instantiate_object_init(PARROT_INTERP, ARGIN(PMC *object), ARGIN(PMC *init))
1306 instantiate_object(interp, object, init);
1312 =item C<void Parrot_instantiate_object>
1314 Wrapper for instantiate_object, passing NULL as initialization.
1315 Returns a new Parrot object.
1317 =cut
1321 PARROT_API
1322 void
1323 Parrot_instantiate_object(PARROT_INTERP, ARGMOD(PMC *object))
1325 instantiate_object(interp, object, NULL);
1331 =item C<static void instantiate_object>
1333 RT#48260: Not yet documented!!!
1335 =cut
1339 static void
1340 instantiate_object(PARROT_INTERP, ARGMOD(PMC *object), ARGIN_NULLOK(PMC *init))
1342 SLOTTYPE *new_object_array;
1343 INTVAL attrib_count, i;
1345 PMC * const _class = object->vtable->pmc_class;
1347 /* put in the real vtable */
1348 PMC * const vtable_pmc = get_attrib_num((SLOTTYPE *)PMC_data(_class),
1349 PCD_OBJECT_VTABLE);
1350 object->vtable = (VTABLE *)PMC_struct_val(vtable_pmc);
1352 /* Grab the attribute count from the class */
1353 attrib_count = CLASS_ATTRIB_COUNT(_class);
1355 /* Build the array that hangs off the new object */
1356 /* First presize it */
1357 set_attrib_array_size(object, attrib_count);
1358 new_object_array = PMC_data_typed(object, SLOTTYPE *);
1360 /* fill with PMCNULL, so that access doesn't segfault */
1361 for (i = 0; i < attrib_count; ++i)
1362 set_attrib_num(object, new_object_array, i, PMCNULL);
1364 /* turn marking on */
1365 set_attrib_flags(object);
1367 /* We are an object now */
1368 PObj_is_object_SET(object);
1370 /* We really ought to call the class init routines here...
1371 * this assumes that an object isa delegate */
1372 do_initcall(interp, _class, object, init);
1378 =item C<PMC * Parrot_remove_parent>
1380 This currently does nothing but return C<PMCNULL>.
1381 RT#50646
1383 =cut
1387 PARROT_API
1388 PARROT_IGNORABLE_RESULT
1389 PARROT_CAN_RETURN_NULL
1390 PMC *
1391 Parrot_remove_parent(PARROT_INTERP, ARGIN(PMC *removed_class),
1392 ARGIN(PMC *existing_class))
1394 UNUSED(interp);
1395 UNUSED(removed_class);
1396 UNUSED(existing_class);
1398 return PMCNULL;
1404 =item C<void mark_object_cache>
1406 Marks all PMCs in the object method cache as live. This shouldn't strictly be
1407 necessary, as they're likely all reachable from namespaces and classes, but
1408 it's unlikely to hurt anything except mark phase performance.
1410 =cut
1414 #define TBL_SIZE_MASK 0x1ff /* x bits 2..10 */
1415 #define TBL_SIZE (1 + TBL_SIZE_MASK)
1417 void
1418 mark_object_cache(PARROT_INTERP)
1420 Caches * const mc = interp->caches;
1421 UINTVAL type, entry;
1423 if (!mc)
1424 return;
1426 for (type = 0; type < mc->mc_size; type++) {
1427 if (!mc->idx[type])
1428 continue;
1430 for (entry = 0; entry < TBL_SIZE; ++entry) {
1431 Meth_cache_entry *e = mc->idx[type][entry];
1432 while (e) {
1433 pobject_lives(interp, (PObj *)e->pmc);
1434 e = e->next;
1443 =item C<void init_object_cache>
1445 Allocate memory for object cache.
1447 =cut
1451 void
1452 init_object_cache(PARROT_INTERP)
1454 Caches * const mc = interp->caches = mem_allocate_zeroed_typed(Caches);
1455 mc->idx = NULL;
1461 =item C<void destroy_object_cache>
1463 RT#48260: Not yet documented!!!
1465 =cut
1469 void
1470 destroy_object_cache(PARROT_INTERP)
1472 UINTVAL i;
1473 Caches * const mc = interp->caches;
1475 /* mc->idx[type][bits] = e; */
1476 for (i = 0; i < mc->mc_size; i++) {
1477 if (mc->idx[i])
1478 invalidate_type_caches(interp, i);
1481 mem_sys_free(mc->idx);
1482 mem_sys_free(mc);
1488 =item C<static void invalidate_type_caches>
1490 RT#48260: Not yet documented!!!
1492 =cut
1496 static void
1497 invalidate_type_caches(PARROT_INTERP, UINTVAL type)
1499 Caches * const mc = interp->caches;
1500 INTVAL i;
1502 if (!mc)
1503 return;
1505 /* is it a valid entry */
1506 if (type >= mc->mc_size || !mc->idx[type])
1507 return;
1509 for (i = 0; i < TBL_SIZE; ++i) {
1510 Meth_cache_entry *e = mc->idx[type][i];
1511 while (e) {
1512 Meth_cache_entry * const next = e->next;
1513 mem_sys_free(e);
1514 e = next;
1518 mem_sys_free(mc->idx[type]);
1519 mc->idx[type] = NULL;
1525 =item C<static void invalidate_all_caches>
1527 RT#48260: Not yet documented!!!
1529 =cut
1533 static void
1534 invalidate_all_caches(PARROT_INTERP)
1536 UINTVAL i;
1537 for (i = 1; i < (UINTVAL)interp->n_vtable_max; ++i)
1538 invalidate_type_caches(interp, i);
1544 =item C<void Parrot_invalidate_method_cache>
1546 Clear method cache for the given class. If class is NULL, caches for
1547 all classes are invalidated.
1549 =cut
1553 PARROT_API
1554 void
1555 Parrot_invalidate_method_cache(PARROT_INTERP, ARGIN_NULLOK(STRING *_class), ARGIN(STRING *meth))
1557 INTVAL type;
1559 /* during interp creation and NCI registration the class_hash
1560 * isn't yet up */
1561 if (!interp->class_hash)
1562 return;
1564 if (interp->resume_flag & RESUME_INITIAL)
1565 return;
1567 if (!_class) {
1568 invalidate_all_caches(interp);
1569 return;
1572 type = pmc_type(interp, _class);
1574 if (type == 0)
1575 invalidate_all_caches(interp);
1576 else if (type > 0)
1577 invalidate_type_caches(interp, (UINTVAL)type);
1582 * quick'n'dirty method cache
1583 * RT#45987: use a hash if method_name is not constant
1584 * i.e. from obj.$Sreg(args)
1585 * If this hash is implemented mark it during DOD
1590 =item C<PMC * Parrot_find_method_direct>
1592 Find a method PMC for a named method, given the class PMC, current
1593 interpreter, and name of the method. Don't use a possible method cache.
1595 =cut
1599 PARROT_API
1600 PARROT_CAN_RETURN_NULL
1601 PARROT_WARN_UNUSED_RESULT
1602 PMC *
1603 Parrot_find_method_direct(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
1605 PMC * const found = find_method_direct_1(interp, _class, method_name);
1607 if (!PMC_IS_NULL(found))
1608 return found;
1611 if (!string_equal(interp, method_name, CONST_STRING(interp, "__get_string")))
1612 return find_method_direct_1(interp, _class,
1613 CONST_STRING(interp, "__get_repr"));
1615 return PMCNULL;
1621 =item C<PMC * Parrot_find_method_with_cache>
1623 Find a method PMC for a named method, given the class PMC, current
1624 interp, and name of the method.
1626 This routine should use the current scope's method cache, if there is
1627 one. If not, it creates a new method cache. Or, rather, it will when
1628 we've got that bit working. For now it unconditionally goes and looks up
1629 the name in the global stash.
1631 =cut
1635 PARROT_API
1636 PARROT_CAN_RETURN_NULL
1637 PARROT_WARN_UNUSED_RESULT
1638 PMC *
1639 Parrot_find_method_with_cache(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
1641 UINTVAL type, bits;
1643 Caches *mc;
1644 Meth_cache_entry *e, *old;
1646 PARROT_ASSERT(method_name != 0);
1648 #if DISABLE_METH_CACHE
1649 return Parrot_find_method_direct(interp, _class, method_name);
1650 #endif
1652 if (! PObj_constant_TEST(method_name))
1653 return Parrot_find_method_direct(interp, _class, method_name);
1655 mc = interp->caches;
1656 type = _class->vtable->base_type;
1657 bits = (((UINTVAL) method_name->strstart) >> 2) & TBL_SIZE_MASK;
1659 if (type >= mc->mc_size) {
1660 if (mc->idx) {
1661 mc->idx = (Meth_cache_entry ***)mem_sys_realloc_zeroed(mc->idx,
1662 sizeof (Meth_cache_entry ***) * (type + 1),
1663 sizeof (Meth_cache_entry ***) * mc->mc_size);
1665 else {
1666 mc->idx = (Meth_cache_entry ***)mem_sys_allocate_zeroed(
1667 sizeof (Meth_cache_entry ***) * (type + 1));
1669 mc->mc_size = type + 1;
1672 if (!mc->idx[type]) {
1673 mc->idx[type] = (Meth_cache_entry **)mem_sys_allocate_zeroed(
1674 sizeof (Meth_cache_entry *) * TBL_SIZE);
1677 e = mc->idx[type][bits];
1678 old = NULL;
1680 while (e && e->strstart != method_name->strstart) {
1681 old = e;
1682 e = e->next;
1685 if (!e) {
1686 /* when here no or no correct entry was at [bits] */
1687 e = mem_allocate_typed(Meth_cache_entry);
1689 if (old)
1690 old->next = e;
1691 else
1692 mc->idx[type][bits] = e;
1694 e->pmc = Parrot_find_method_direct(interp, _class, method_name);
1695 e->next = NULL;
1696 e->strstart = method_name->strstart;
1699 return e->pmc;
1705 =item C<static void debug_trace_find_meth>
1707 RT#48260: Not yet documented!!!
1709 =cut
1713 #ifdef NDEBUG
1714 # define TRACE_FM(i, c, m, sub)
1715 #else
1716 # define TRACE_FM(i, c, m, sub) \
1717 debug_trace_find_meth(i, c, m, sub)
1719 static void
1720 debug_trace_find_meth(PARROT_INTERP, ARGIN(const PMC *_class),
1721 ARGIN(const STRING *name), ARGIN_NULLOK(const PMC *sub))
1723 STRING *class_name;
1724 const char *result;
1725 Interp *tracer;
1727 if (!Interp_trace_TEST(interp, PARROT_TRACE_FIND_METH_FLAG))
1728 return;
1730 if (PObj_is_class_TEST(_class)) {
1731 SLOTTYPE * const class_array = PMC_data_typed(_class, SLOTTYPE *);
1732 PMC *const class_name_pmc = get_attrib_num(class_array, PCD_CLASS_NAME);
1733 class_name = PMC_str_val(class_name_pmc);
1735 else
1736 class_name = _class->vtable->whoami;
1738 if (sub) {
1739 if (sub->vtable->base_type == enum_class_NCI)
1740 result = "NCI";
1741 else
1742 result = "Sub";
1744 else
1745 result = "no";
1747 tracer = interp->debugger ? interp->debugger : interp;
1748 PIO_eprintf(tracer, "# find_method class '%Ss' method '%Ss': %s\n",
1749 class_name, name, result);
1752 #endif
1757 =item C<static PMC * find_method_direct_1>
1759 RT#48260: Not yet documented!!!
1761 =cut
1765 PARROT_WARN_UNUSED_RESULT
1766 PARROT_CAN_RETURN_NULL
1767 static PMC *
1768 find_method_direct_1(PARROT_INTERP, ARGIN(PMC *_class),
1769 ARGIN(STRING *method_name))
1771 INTVAL i;
1773 PMC * const mro = _class->vtable->mro;
1774 const INTVAL n = VTABLE_elements(interp, mro);
1776 for (i = 0; i < n; ++i) {
1777 PMC *method, *ns;
1779 _class = VTABLE_get_pmc_keyed_int(interp, mro, i);
1780 ns = VTABLE_get_namespace(interp, _class);
1781 method = VTABLE_get_pmc_keyed_str(interp, ns, method_name);
1783 TRACE_FM(interp, _class, method_name, method);
1785 if (!PMC_IS_NULL(method))
1786 return method;
1789 TRACE_FM(interp, _class, method_name, NULL);
1790 return PMCNULL;
1796 =item C<INTVAL Parrot_add_attribute>
1798 Adds the attribute C<attr> to the class.
1800 Life is ever so much easier if a class keeps its attributes at the
1801 end of the attribute array, since we don't have to insert and
1802 reorder attributes. Inserting's no big deal, especially since we're
1803 going to break horribly if you insert into a class that's been
1804 subclassed, but it'll do for now.
1806 =cut
1810 PARROT_API
1811 INTVAL
1812 Parrot_add_attribute(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *attr))
1814 STRING *full_attr_name;
1815 SLOTTYPE * const class_array = (SLOTTYPE *)PMC_data(_class);
1816 STRING * const class_name = VTABLE_get_string(interp,
1817 get_attrib_num(class_array, PCD_CLASS_NAME));
1818 PMC * const attr_array = get_attrib_num(class_array, PCD_CLASS_ATTRIBUTES);
1819 PMC * const attr_hash = get_attrib_num(class_array, PCD_ATTRIBUTES);
1820 INTVAL idx = VTABLE_elements(interp, attr_array);
1822 VTABLE_set_integer_native(interp, attr_array, idx + 1);
1823 VTABLE_set_string_keyed_int(interp, attr_array, idx, attr);
1825 full_attr_name = string_concat(interp, class_name,
1826 string_from_cstring(interp, "\0", 1), 0);
1828 full_attr_name = string_concat(interp, full_attr_name, attr, 0);
1830 /* RT#45989 escape NUL char */
1831 if (VTABLE_exists_keyed_str(interp, attr_hash, full_attr_name))
1832 real_exception(interp, NULL, 1,
1833 "Attribute '%Ss' already exists", full_attr_name);
1836 * RT#45993 check if someone is trying to add attributes to a parent class
1837 * while there are already child class attrs
1839 idx = CLASS_ATTRIB_COUNT(_class)++;
1840 VTABLE_set_integer_keyed_str(interp, attr_hash, attr, idx);
1841 VTABLE_set_integer_keyed_str(interp, attr_hash, full_attr_name, idx);
1843 return idx;
1847 /* ************************************************************************ */
1848 /* ********* BELOW HERE IS NEW PPD15 IMPLEMENTATION RELATED STUFF ********* */
1849 /* ************************************************************************ */
1853 =item C<static PMC* C3_merge>
1855 RT#48260: Not yet documented!!!
1857 =cut
1861 PARROT_WARN_UNUSED_RESULT
1862 PARROT_CAN_RETURN_NULL
1863 static PMC*
1864 C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
1866 PMC *accepted = PMCNULL;
1867 PMC *result = pmc_new(interp, enum_class_ResizablePMCArray);
1868 const int list_count = VTABLE_elements(interp, merge_list);
1869 int cand_count = 0;
1870 int i;
1872 /* Try and find something appropriate to add to the MRO - basically, the
1873 * first list head that is not in the tail of all the other lists. */
1874 for (i = 0; i < list_count; i++) {
1875 PMC * const cand_list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1877 PMC *cand_class;
1878 int reject = 0;
1879 int j;
1881 if (VTABLE_elements(interp, cand_list) == 0)
1882 continue;
1884 cand_class = VTABLE_get_pmc_keyed_int(interp, cand_list, 0);
1885 cand_count++;
1887 for (j = 0; j < list_count; j++) {
1888 /* Skip the current list. */
1889 if (j != i) {
1890 /* Is it in the tail? If so, reject. */
1891 PMC * const check_list =
1892 VTABLE_get_pmc_keyed_int(interp, merge_list, j);
1894 const int check_length = VTABLE_elements(interp, check_list);
1895 int k;
1897 for (k = 1; k < check_length; k++) {
1898 if (VTABLE_get_pmc_keyed_int(interp, check_list, k) ==
1899 cand_class) {
1900 reject = 1;
1901 break;
1907 /* If we didn't reject it, this candidate will do. */
1908 if (!reject) {
1909 accepted = cand_class;
1910 break;
1914 /* If we never found any candidates, return an empty list. */
1915 if (cand_count == 0)
1916 return pmc_new(interp, enum_class_ResizablePMCArray);
1918 /* If we didn't find anything to accept, error. */
1919 if (PMC_IS_NULL(accepted))
1920 real_exception(interp, NULL, ILL_INHERIT,
1921 "Could not build C3 linearization: ambiguous hierarchy");
1923 /* Otherwise, remove what was accepted from the merge lists. */
1924 for (i = 0; i < list_count; i++) {
1925 int j;
1927 PMC * const list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1928 const int list_count = VTABLE_elements(interp, list);
1930 for (j = 0; j < list_count; j++) {
1931 if (VTABLE_get_pmc_keyed_int(interp, list, j) == accepted) {
1932 VTABLE_delete_keyed_int(interp, list, j);
1933 break;
1938 /* Need to merge what remains of the list, then put what was accepted on
1939 * the start of the list, and we're done. */
1940 result = C3_merge(interp, merge_list);
1941 VTABLE_unshift_pmc(interp, result, accepted);
1943 return result;
1949 =item C<PMC* Parrot_ComputeMRO_C3>
1951 Computes the C3 linearization for the given class.
1953 =cut
1957 PARROT_API
1958 PARROT_WARN_UNUSED_RESULT
1959 PARROT_CAN_RETURN_NULL
1960 PMC*
1961 Parrot_ComputeMRO_C3(PARROT_INTERP, ARGIN(PMC *_class))
1963 PMC *result;
1964 PMC * const merge_list = pmc_new(interp, enum_class_ResizablePMCArray);
1965 PMC *immediate_parents;
1966 int i, parent_count;
1968 /* Now get immediate parents list. */
1969 Parrot_PCCINVOKE(interp, _class, CONST_STRING(interp, "parents"),
1970 "->P", &immediate_parents);
1972 if (!immediate_parents)
1973 real_exception(interp, NULL, METH_NOT_FOUND,
1974 "Failed to get parents list from class!");
1976 parent_count = VTABLE_elements(interp, immediate_parents);
1978 if (parent_count == 0) {
1979 /* No parents - MRO just contains this class. */
1980 result = pmc_new(interp, enum_class_ResizablePMCArray);
1981 VTABLE_push_pmc(interp, result, _class);
1982 return result;
1985 /* Otherwise, need to do merge. For that, need linearizations of all of
1986 * our parents added to the merge list. */
1987 for (i = 0; i < parent_count; i++) {
1988 PMC * const lin = Parrot_ComputeMRO_C3(interp,
1989 VTABLE_get_pmc_keyed_int(interp, immediate_parents, i));
1991 if (PMC_IS_NULL(lin))
1992 return PMCNULL;
1994 VTABLE_push_pmc(interp, merge_list, lin);
1997 /* Finally, need list of direct parents on the end of the merge list, then
1998 * we can merge. */
1999 VTABLE_push_pmc(interp, merge_list, immediate_parents);
2000 result = C3_merge(interp, merge_list);
2002 if (PMC_IS_NULL(result))
2003 return PMCNULL;
2005 /* Merged result needs this class on the start, and then we're done. */
2006 VTABLE_unshift_pmc(interp, result, _class);
2008 return result;
2014 =item C<void Parrot_ComposeRole>
2016 Used by the Class and Object PMCs internally to compose a role into either of
2017 them. The C<role> parameter is the role that we are composing into the class
2018 or role. C<methods_hash> is the hash of method names to invokable PMCs that
2019 contains the methods the class or role has. C<roles_list> is the list of roles
2020 the the class or method does.
2022 The C<role> parameter is only dealt with by its external interface. Whether
2023 this routine is usable by any other object system implemented in Parrot very
2024 much depends on how closely the role composition semantics they want are to
2025 the default implementation.
2027 =cut
2031 PARROT_API
2032 void
2033 Parrot_ComposeRole(PARROT_INTERP, ARGIN(PMC *role),
2034 ARGIN(PMC *exclude), int got_exclude,
2035 ARGIN(PMC *alias), int got_alias,
2036 ARGIN(PMC *methods_hash), ARGIN(PMC *roles_list))
2038 PMC *methods;
2039 PMC *methods_iter;
2040 PMC *roles_of_role;
2041 PMC *proposed_add_methods;
2043 int i, roles_of_role_count;
2045 /* Check we have not already composed the role; if so, just ignore it. */
2046 int roles_count = VTABLE_elements(interp, roles_list);
2048 for (i = 0; i < roles_count; i++)
2049 if (VTABLE_get_pmc_keyed_int(interp, roles_list, i) == role)
2050 return;
2052 /* Get the methods from the role. */
2053 Parrot_PCCINVOKE(interp, role,
2054 CONST_STRING(interp, "methods"), "->P", &methods);
2056 if (PMC_IS_NULL(methods))
2057 return;
2059 /* We need to check for conflicts before we do the composition. We
2060 * put each method that would be OK to add into a proposal list, and
2061 * bail out right away if we find a problem. */
2062 proposed_add_methods = pmc_new(interp, enum_class_Hash);
2063 methods_iter = VTABLE_get_iter(interp, methods);
2065 while (VTABLE_get_bool(interp, methods_iter)) {
2066 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
2067 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp, methods,
2068 method_name);
2070 /* Need to find the name we'll check for a conflict on. */
2071 int excluded = 0;
2073 /* Check if it's in the exclude list. */
2074 if (got_exclude) {
2075 const int exclude_count = VTABLE_elements(interp, exclude);
2077 for (i = 0; i < exclude_count; i++) {
2078 const STRING * const check =
2079 VTABLE_get_string_keyed_int(interp, exclude, i);
2081 if (string_equal(interp, check, method_name) == 0) {
2082 excluded = 1;
2083 break;
2088 /* If we weren't excluded... */
2089 if (!excluded) {
2090 /* Is there a method with this name already in the class?
2091 * RT#45999 multi-method handling. */
2093 if (VTABLE_exists_keyed_str(interp, methods_hash, method_name))
2094 /* Conflicts with something already in the class. */
2095 real_exception(interp, NULL, ROLE_COMPOSITION_METH_CONFLICT,
2096 "A conflict occurred during role composition "
2097 "due to method '%S'.", method_name);
2099 /* What about a conflict with ourslef? */
2100 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
2101 method_name))
2102 /* Something very weird is going on. */
2103 real_exception(interp, NULL, ROLE_COMPOSITION_METH_CONFLICT,
2104 "A conflict occurred during role composition;"
2105 " the method '%S' from the role managed to conflict "
2106 "with itself somehow.", method_name);
2108 /* If we got here, no conflicts! Add method to the "to compose"
2109 * list. */
2110 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
2111 method_name, cur_method);
2114 /* Now see if we've got an alias. */
2115 if (got_alias && VTABLE_exists_keyed_str(interp, alias, method_name)) {
2116 /* Got one. Get name to alias it to. */
2117 STRING * const alias_name = VTABLE_get_string_keyed_str(interp,
2118 alias, method_name);
2120 /* Is there a method with this name already in the class?
2121 * RT#45999: multi-method handling. */
2122 if (VTABLE_exists_keyed_str(interp, methods_hash, alias_name))
2123 /* Conflicts with something already in the class. */
2124 real_exception(interp, NULL, ROLE_COMPOSITION_METH_CONFLICT,
2125 "A conflict occurred during role composition"
2126 " due to the aliasing of '%S' to '%S'.",
2127 method_name, alias_name);
2129 /* What about a conflict with ourslef? */
2130 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
2131 alias_name))
2132 real_exception(interp, NULL, ROLE_COMPOSITION_METH_CONFLICT,
2133 "A conflict occurred during role composition"
2134 " due to the aliasing of '%S' to '%S' (role already has"
2135 " a method '%S').",
2136 method_name, alias_name, alias_name);
2138 /* If we get here, no conflicts! Add method to the "to compose"
2139 * list with its alias. */
2140 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
2141 alias_name, cur_method);
2145 /* If we get here, we detected no conflicts. Go ahead and compose the
2146 * methods. */
2147 methods_iter = VTABLE_get_iter(interp, proposed_add_methods);
2149 while (VTABLE_get_bool(interp, methods_iter)) {
2150 /* Get current method and its name. */
2151 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
2152 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp,
2153 proposed_add_methods, method_name);
2155 /* Add it to the methods of the class. */
2156 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
2159 /* Add this role to the roles list. */
2160 VTABLE_push_pmc(interp, roles_list, role);
2161 roles_count++;
2163 /* As a result of composing this role, we will also now do the roles
2164 * that it did itself. Note that we already have the correct methods
2165 * as roles "flatten" the methods they get from other roles into their
2166 * own method list. */
2167 Parrot_PCCINVOKE(interp, role,
2168 CONST_STRING(interp, "roles"), "->P", &roles_of_role);
2169 roles_of_role_count = VTABLE_elements(interp, roles_of_role);
2171 for (i = 0; i < roles_of_role_count; i++) {
2172 /* Only add if we don't already have it in the list. */
2173 PMC * const cur_role = VTABLE_get_pmc_keyed_int(interp,
2174 roles_of_role, i);
2175 int j;
2177 for (j = 0; j < roles_count; j++) {
2178 if (VTABLE_get_pmc_keyed_int(interp, roles_list, j) == cur_role) {
2179 /* We ain't be havin' it. */
2180 VTABLE_push_pmc(interp, roles_list, cur_role);
2189 =back
2191 =head1 SEE ALSO
2193 F<include/parrot/oo.h>, F<include/parrot/oo_private.h>,
2194 F<docs/pdds/pdd15_objects.pod>.
2196 =cut
2201 * Local variables:
2202 * c-file-style: "parrot"
2203 * End:
2204 * vim: expandtab shiftwidth=4: