fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / oo.c
blobbc4559c4025dd169239788a5c75d0c29f0366670
1 /*
2 Copyright (C) 2007-2010, 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_INLINE
54 PARROT_CANNOT_RETURN_NULL
55 PARROT_WARN_UNUSED_RESULT
56 static PMC * get_pmc_proxy(PARROT_INTERP, INTVAL type)
57 __attribute__nonnull__(1);
59 static void invalidate_all_caches(PARROT_INTERP)
60 __attribute__nonnull__(1);
62 static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)
63 __attribute__nonnull__(1);
65 #define ASSERT_ARGS_C3_merge __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
66 PARROT_ASSERT_ARG(interp) \
67 , PARROT_ASSERT_ARG(merge_list))
68 #define ASSERT_ARGS_debug_trace_find_meth __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
69 PARROT_ASSERT_ARG(interp) \
70 , PARROT_ASSERT_ARG(_class) \
71 , PARROT_ASSERT_ARG(name))
72 #define ASSERT_ARGS_fail_if_type_exists __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
73 PARROT_ASSERT_ARG(interp) \
74 , PARROT_ASSERT_ARG(name))
75 #define ASSERT_ARGS_get_pmc_proxy __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
76 PARROT_ASSERT_ARG(interp))
77 #define ASSERT_ARGS_invalidate_all_caches __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
78 PARROT_ASSERT_ARG(interp))
79 #define ASSERT_ARGS_invalidate_type_caches __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
80 PARROT_ASSERT_ARG(interp))
81 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
82 /* HEADERIZER END: static */
87 =item C<static void debug_trace_find_meth(PARROT_INTERP, const PMC *_class,
88 const STRING *name, const PMC *sub)>
90 Print some information about the search for a sub.
92 =cut
96 #ifdef NDEBUG
97 # define TRACE_FM(i, c, m, sub)
98 #else
99 # define TRACE_FM(i, c, m, sub) \
100 debug_trace_find_meth((i), (c), (m), (sub))
102 static void
103 debug_trace_find_meth(PARROT_INTERP, ARGIN(const PMC *_class),
104 ARGIN(const STRING *name), ARGIN_NULLOK(const PMC *sub))
106 ASSERT_ARGS(debug_trace_find_meth)
107 STRING *class_name;
108 const char *result;
109 Interp *tracer;
111 if (!Interp_trace_TEST(interp, PARROT_TRACE_FIND_METH_FLAG))
112 return;
114 if (PObj_is_class_TEST(_class)) {
115 SLOTTYPE * const class_array = PMC_data_typed(_class, SLOTTYPE *);
116 PMC * const class_name_pmc = get_attrib_num(class_array, PCD_CLASS_NAME);
117 class_name = VTABLE_get_string(interp, class_name_pmc);
119 else
120 class_name = _class->vtable->whoami;
122 if (sub) {
123 if (sub->vtable->base_type == enum_class_NativePCCMethod)
124 result = "NativePCCMethod";
125 else if (sub->vtable->base_type == enum_class_NCI)
126 result = "NCI";
127 else
128 result = "Sub";
130 else
131 result = "no";
133 tracer = (interp->pdb && interp->pdb->debugger) ?
134 interp->pdb->debugger :
135 interp;
136 Parrot_io_eprintf(tracer, "# find_method class '%Ss' method '%Ss': %s\n",
137 class_name, name, result);
140 #endif
145 =item C<void Parrot_oo_extract_methods_from_namespace(PARROT_INTERP, PMC *self,
146 PMC *ns)>
148 Extract methods and vtable overrides from the given namespace and insert them
149 into the class.
151 =cut
155 void
156 Parrot_oo_extract_methods_from_namespace(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *ns))
158 ASSERT_ARGS(Parrot_oo_extract_methods_from_namespace)
159 PMC *methods, *vtable_overrides;
161 /* Pull in methods from the namespace, if any. */
162 if (PMC_IS_NULL(ns))
163 return;
165 /* Import any methods. */
166 Parrot_pcc_invoke_method_from_c_args(interp, ns, CONST_STRING(interp, "get_associated_methods"), "->P", &methods);
168 if (!PMC_IS_NULL(methods)) {
169 PMC * const iter = VTABLE_get_iter(interp, methods);
171 while (VTABLE_get_bool(interp, iter)) {
172 STRING * const meth_name = VTABLE_shift_string(interp, iter);
173 PMC * const meth_sub = VTABLE_get_pmc_keyed_str(interp, methods,
174 meth_name);
175 VTABLE_add_method(interp, self, meth_name, meth_sub);
179 /* Import any vtables. */
180 Parrot_pcc_invoke_method_from_c_args(interp, ns, CONST_STRING(interp, "get_associated_vtable_methods"), "->P", &vtable_overrides);
182 if (!PMC_IS_NULL(vtable_overrides)) {
183 PMC * const iter = VTABLE_get_iter(interp, vtable_overrides);
184 while (VTABLE_get_bool(interp, iter)) {
185 STRING * const vtable_index_str = VTABLE_shift_string(interp, iter);
186 PMC * const vtable_sub = VTABLE_get_pmc_keyed_str(interp,
187 vtable_overrides, vtable_index_str);
189 /* Look up the name of the vtable function from the index. */
190 const INTVAL vtable_index = Parrot_str_to_int(interp, vtable_index_str);
191 const char * const meth_c = Parrot_vtable_slot_names[vtable_index];
192 STRING * const vtable_name = Parrot_str_new(interp, meth_c, 0);
193 VTABLE_add_vtable_override(interp, self, vtable_name, vtable_sub);
201 =item C<PMC * Parrot_oo_get_class(PARROT_INTERP, PMC *key)>
203 Lookup a class object from a namespace, string, or key PMC.
205 TODO: This function is terribly inefficient. It needs to be refactored in a
206 major way
208 =cut
212 PARROT_EXPORT
213 PARROT_CAN_RETURN_NULL
214 PARROT_WARN_UNUSED_RESULT
215 PMC *
216 Parrot_oo_get_class(PARROT_INTERP, ARGIN(PMC *key))
218 ASSERT_ARGS(Parrot_oo_get_class)
219 PMC *classobj = PMCNULL;
221 if (PMC_IS_NULL(key))
222 return PMCNULL;
224 if (PObj_is_class_TEST(key))
225 classobj = key;
226 else {
227 /* Fast select of behavior based on type of the lookup key */
228 switch (key->vtable->base_type) {
229 case enum_class_NameSpace:
230 classobj = VTABLE_get_class(interp, key);
231 break;
232 case enum_class_String:
233 case enum_class_Key:
234 case enum_class_ResizableStringArray:
236 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp,
237 interp->HLL_namespace,
238 Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
239 PMC * const ns = Parrot_ns_get_namespace_keyed(interp,
240 hll_ns, key);
242 if (!PMC_IS_NULL(ns))
243 classobj = VTABLE_get_class(interp, ns);
245 default:
246 break;
250 /* If the PMCProxy doesn't exist yet for the given key, we look up the
251 type ID here and create a new one */
252 if (PMC_IS_NULL(classobj)) {
253 INTVAL type;
254 const INTVAL base_type = key->vtable->base_type;
256 /* This is a hack! All PMCs should be able to be handled through
257 a single codepath, and all of them should be able to avoid
258 stringification because it's so imprecise. */
259 if (base_type == enum_class_Key
260 || base_type == enum_class_ResizableStringArray
261 || base_type == enum_class_String)
262 type = Parrot_pmc_get_type(interp, key);
263 else
264 type = Parrot_pmc_get_type_str(interp, VTABLE_get_string(interp, key));
266 classobj = get_pmc_proxy(interp, type);
269 return classobj;
274 =item C<PMC * Parrot_oo_clone_object(PARROT_INTERP, PMC *pmc, PMC *dest)>
276 Clone an Object PMC. If an existing PMC C<dest> is provided, reuse that
277 PMC to store copies of the data. Otherwise, create a new PMC and populate
278 that with the data.
280 =cut
284 PARROT_CANNOT_RETURN_NULL
285 PMC *
286 Parrot_oo_clone_object(PARROT_INTERP, ARGIN(PMC *pmc), ARGMOD_NULLOK(PMC *dest))
288 ASSERT_ARGS(Parrot_oo_clone_object)
289 Parrot_Object_attributes *obj = PARROT_OBJECT(pmc);
290 Parrot_Object_attributes *cloned_guts;
291 Parrot_Class_attributes *_class;
292 PMC *cloned;
293 INTVAL num_classes;
294 INTVAL i, num_attrs;
296 if (!PMC_IS_NULL(dest)) {
297 cloned = dest;
299 else {
300 cloned = Parrot_pmc_new_noinit(interp, enum_class_Object);
303 _class = PARROT_CLASS(obj->_class);
304 PARROT_ASSERT(_class);
305 num_classes = VTABLE_elements(interp, _class->all_parents);
307 /* Set custom GC mark and destroy on the object. */
308 PObj_custom_mark_SET(cloned);
309 PObj_custom_destroy_SET(cloned);
311 /* Flag that it is an object */
312 PObj_is_object_SET(cloned);
314 /* Now clone attributes list.class. */
315 cloned_guts = (Parrot_Object_attributes *) PMC_data(cloned);
316 cloned_guts->_class = obj->_class;
317 cloned_guts->attrib_store = VTABLE_clone(interp, obj->attrib_store);
318 num_attrs = VTABLE_elements(interp, cloned_guts->attrib_store);
319 for (i = 0; i < num_attrs; ++i) {
320 PMC * const to_clone = VTABLE_get_pmc_keyed_int(interp, cloned_guts->attrib_store, i);
321 if (!PMC_IS_NULL(to_clone)) {
322 VTABLE_set_pmc_keyed_int(interp, cloned_guts->attrib_store, i,
323 VTABLE_clone(interp, to_clone));
327 /* Some of the attributes may have been the PMCs providing storage for any
328 * PMCs we inherited from; also need to clone those. */
329 if (CLASS_has_alien_parents_TEST(obj->_class)) {
330 int j;
331 /* Locate any PMC parents. */
332 for (j = 0; j < num_classes; ++j) {
333 PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp, _class->all_parents, j);
334 if (cur_class->vtable->base_type == enum_class_PMCProxy) {
335 /* Clone this PMC too. */
336 STRING * const proxy = CONST_STRING(interp, "proxy");
337 VTABLE_set_attr_keyed(interp, cloned, cur_class, proxy,
338 VTABLE_clone(interp,
339 VTABLE_get_attr_keyed(interp, cloned, cur_class, proxy)));
344 /* And we have ourselves a clone. */
345 return cloned;
350 =item C<static PMC * get_pmc_proxy(PARROT_INTERP, INTVAL type)>
352 Get the PMC proxy for a PMC with the given type, creating it if does not exist.
353 If type is not a valid type, return PMCNULL. This code assumes that
354 all PMCProxy objects live in the 'parrot' HLL namespace -- if/when
355 we allow PMC types to exist in other HLL namespaces, this code will
356 need to be updated.
358 For internal use only.
360 =cut
364 PARROT_INLINE
365 PARROT_CANNOT_RETURN_NULL
366 PARROT_WARN_UNUSED_RESULT
367 static PMC *
368 get_pmc_proxy(PARROT_INTERP, INTVAL type)
370 ASSERT_ARGS(get_pmc_proxy)
371 PMC * type_class;
373 /* Check if not a PMC or invalid type number */
374 if (type > interp->n_vtable_max || type <= 0)
375 return PMCNULL;
377 type_class = interp->vtables[type]->pmc_class;
378 if (type != enum_class_Class
379 && type_class->vtable->base_type == enum_class_Class) {
380 return type_class;
382 else {
383 PMC * const parrot_hll = Parrot_ns_get_namespace_keyed_str(interp, interp->root_namespace, CONST_STRING(interp, "parrot"));
384 PMC * const pmc_ns =
385 Parrot_ns_make_namespace_keyed_str(interp, parrot_hll,
386 interp->vtables[type]->whoami);
387 PMC * proxy = VTABLE_get_class(interp, pmc_ns);
389 /* Create proxy if not found */
390 if (PMC_IS_NULL(proxy)) {
391 proxy = Parrot_pmc_new_init_int(interp, enum_class_PMCProxy, type);
392 Parrot_pcc_invoke_method_from_c_args(interp, pmc_ns, CONST_STRING(interp, "set_class"), "P->", proxy);
394 return proxy;
400 =item C<PMC * Parrot_oo_get_class_str(PARROT_INTERP, STRING *name)>
402 Lookup a class object from the string C<name>. If the metaobject is found,
403 return it. Otherwise, create a new PMCProxy for the type ID number.
405 =cut
409 PARROT_EXPORT
410 PARROT_CAN_RETURN_NULL
411 PARROT_WARN_UNUSED_RESULT
412 PMC *
413 Parrot_oo_get_class_str(PARROT_INTERP, ARGIN_NULLOK(STRING *name))
415 ASSERT_ARGS(Parrot_oo_get_class_str)
417 if (STRING_IS_NULL(name))
418 return PMCNULL;
419 else {
421 /* First check in current HLL namespace */
422 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp, interp->HLL_namespace,
423 Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
424 PMC * const ns = Parrot_ns_get_namespace_keyed_str(interp, hll_ns, name);
425 PMC * const _class = PMC_IS_NULL(ns)
426 ? PMCNULL : VTABLE_get_class(interp, ns);
428 /* If not found, check for a PMC */
429 if (PMC_IS_NULL(_class))
430 return get_pmc_proxy(interp, Parrot_pmc_get_type_str(interp, name));
431 else
432 return _class;
439 =item C<PMC * Parrot_oo_newclass_from_str(PARROT_INTERP, STRING *name)>
441 Create a new Class PMC for a new type of the given C<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 = Parrot_pmc_new(interp, enum_class_String);
454 PMC * const namehash = Parrot_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 = Parrot_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 Find the vtable override with the specified C<name> in the given C<classobj>
473 metaobject.
475 =cut
479 PARROT_EXPORT
480 PARROT_CAN_RETURN_NULL
481 PARROT_WARN_UNUSED_RESULT
482 PMC *
483 Parrot_oo_find_vtable_override_for_class(PARROT_INTERP,
484 ARGIN(PMC *classobj), ARGIN(STRING *name))
486 ASSERT_ARGS(Parrot_oo_find_vtable_override_for_class)
487 const Parrot_Class_attributes * const class_info = PARROT_CLASS(classobj);
488 PARROT_ASSERT(PObj_is_class_TEST(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. */
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 = Parrot_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 = Parrot_pmc_get_new_vtable_index(interp);
700 if (!typeid_exists) {
701 /* set entry in name->type hash */
702 PMC * const classname_hash = interp->class_hash;
703 PMC * const item = Parrot_pmc_new_init_int(interp,
704 enum_class_Integer, type);
705 VTABLE_set_pmc_keyed(interp, classname_hash, name, item);
708 UNLOCK_INTERPRETER(interp);
710 return type;
715 =item C<void mark_object_cache(PARROT_INTERP)>
717 Marks all PMCs in the object method cache as live. This shouldn't strictly be
718 necessary, as they're likely all reachable from namespaces and classes, but
719 it's unlikely to hurt anything except mark phase performance.
721 =cut
725 #define TBL_SIZE_MASK 0x1ff /* x bits 2..10 */
726 #define TBL_SIZE (1 + TBL_SIZE_MASK)
728 void
729 mark_object_cache(PARROT_INTERP)
731 ASSERT_ARGS(mark_object_cache)
732 Caches * const mc = interp->caches;
733 UINTVAL type, entry;
735 if (!mc)
736 return;
738 for (type = 0; type < mc->mc_size; ++type) {
739 if (!mc->idx[type])
740 continue;
742 for (entry = 0; entry < TBL_SIZE; ++entry) {
743 Meth_cache_entry *e = mc->idx[type][entry];
744 while (e) {
745 Parrot_gc_mark_PMC_alive(interp, e->pmc);
746 e = e->next;
755 =item C<void init_object_cache(PARROT_INTERP)>
757 Allocate memory for object cache.
759 =cut
763 void
764 init_object_cache(PARROT_INTERP)
766 ASSERT_ARGS(init_object_cache)
767 Caches * const mc = interp->caches = mem_gc_allocate_zeroed_typed(interp, Caches);
768 mc->idx = NULL;
774 =item C<void destroy_object_cache(PARROT_INTERP)>
776 Destroy the object cache. Loop over all caches and invalidate them. Then
777 free the caches back to the OS.
779 =cut
783 void
784 destroy_object_cache(PARROT_INTERP)
786 ASSERT_ARGS(destroy_object_cache)
787 UINTVAL i;
788 Caches * const mc = interp->caches;
790 /* mc->idx[type][bits] = e; */
791 for (i = 0; i < mc->mc_size; ++i) {
792 if (mc->idx[i])
793 invalidate_type_caches(interp, i);
796 mem_gc_free(interp, mc->idx);
797 mem_gc_free(interp, mc);
803 =item C<static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)>
805 Invalidate the cache of the specified type. Free each entry and then free
806 the entire cache.
808 =cut
812 static void
813 invalidate_type_caches(PARROT_INTERP, UINTVAL type)
815 ASSERT_ARGS(invalidate_type_caches)
816 Caches * const mc = interp->caches;
817 INTVAL i;
819 if (!mc)
820 return;
822 /* is it a valid entry */
823 if (type >= mc->mc_size || !mc->idx[type])
824 return;
826 for (i = 0; i < TBL_SIZE; ++i) {
827 Meth_cache_entry *e = mc->idx[type][i];
828 while (e) {
829 Meth_cache_entry * const next = e->next;
830 mem_gc_free(interp, e);
831 e = next;
835 mem_gc_free(interp, mc->idx[type]);
836 mc->idx[type] = NULL;
842 =item C<static void invalidate_all_caches(PARROT_INTERP)>
844 Invalidate all caches by looping over each cache and calling
845 C<invalidate_type_caches> on them.
847 =cut
851 static void
852 invalidate_all_caches(PARROT_INTERP)
854 ASSERT_ARGS(invalidate_all_caches)
855 int i;
856 for (i = 1; i < interp->n_vtable_max; ++i)
857 invalidate_type_caches(interp, i);
863 =item C<void Parrot_invalidate_method_cache(PARROT_INTERP, STRING *_class)>
865 Clear method cache for the given class. If class is NULL, caches for
866 all classes are invalidated.
868 =cut
872 PARROT_EXPORT
873 void
874 Parrot_invalidate_method_cache(PARROT_INTERP, ARGIN_NULLOK(STRING *_class))
876 ASSERT_ARGS(Parrot_invalidate_method_cache)
877 INTVAL type;
879 /* during interp creation and NCI registration the class_hash
880 * isn't yet up */
881 if (!interp->class_hash)
882 return;
884 if (interp->resume_flag & RESUME_INITIAL)
885 return;
887 if (!_class) {
888 invalidate_all_caches(interp);
889 return;
892 type = Parrot_pmc_get_type_str(interp, _class);
894 if (type == 0)
895 invalidate_all_caches(interp);
896 else if (type > 0)
897 invalidate_type_caches(interp, (UINTVAL)type);
902 =item C<PMC * Parrot_find_method_direct(PARROT_INTERP, PMC *_class, STRING
903 *method_name)>
905 Find a method PMC for a named method, given the class PMC, current
906 interpreter, and name of the method. Don't use a possible method cache.
908 =cut
912 PARROT_EXPORT
913 PARROT_CAN_RETURN_NULL
914 PARROT_WARN_UNUSED_RESULT
915 PMC *
916 Parrot_find_method_direct(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
918 ASSERT_ARGS(Parrot_find_method_direct)
920 STRING * const class_str = CONST_STRING(interp, "class");
921 STRING * const methods_str = CONST_STRING(interp, "methods");
922 PMC * const mro = _class->vtable->mro;
923 const INTVAL n = VTABLE_elements(interp, mro);
924 INTVAL i;
926 for (i = 0; i < n; ++i) {
927 PMC * const _class = VTABLE_get_pmc_keyed_int(interp, mro, i);
928 PMC * const ns = VTABLE_get_namespace(interp, _class);
929 PMC * const class_obj = VTABLE_inspect_str(interp, ns, class_str);
930 PMC *method = PMCNULL;
931 PMC *method_hash;
933 if (PMC_IS_NULL(class_obj))
934 method_hash = VTABLE_inspect_str(interp, ns, methods_str);
935 else
936 method_hash = VTABLE_inspect_str(interp, class_obj, methods_str);
938 if (!PMC_IS_NULL(method_hash))
939 method = VTABLE_get_pmc_keyed_str(interp, method_hash, method_name);
941 if (PMC_IS_NULL(method))
942 method = VTABLE_get_pmc_keyed_str(interp, ns, method_name);
944 TRACE_FM(interp, _class, method_name, method);
946 if (!PMC_IS_NULL(method))
947 return method;
950 TRACE_FM(interp, _class, method_name, NULL);
951 return PMCNULL;
957 =item C<PMC * Parrot_find_method_with_cache(PARROT_INTERP, PMC *_class, STRING
958 *method_name)>
960 Find a method PMC for a named method, given the class PMC, current
961 interp, and name of the method.
963 This routine should use the current scope's method cache, if there is
964 one. If not, it creates a new method cache. Or, rather, it will when
965 we've got that bit working. For now it unconditionally goes and looks up
966 the name in the global stash.
968 =cut
972 PARROT_EXPORT
973 PARROT_CAN_RETURN_NULL
974 PARROT_WARN_UNUSED_RESULT
975 PMC *
976 Parrot_find_method_with_cache(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
978 ASSERT_ARGS(Parrot_find_method_with_cache)
980 #if DISABLE_METH_CACHE
981 return Parrot_find_method_direct(interp, _class, method_name);
982 #else
984 Caches *mc;
985 Meth_cache_entry *e;
986 UINTVAL type, bits;
988 if (! PObj_constant_TEST(method_name))
989 return Parrot_find_method_direct(interp, _class, method_name);
991 mc = interp->caches;
992 type = _class->vtable->base_type;
993 bits = (((UINTVAL) Buffer_bufstart(method_name)) >> 2) & TBL_SIZE_MASK;
995 if (type >= mc->mc_size) {
996 if (mc->idx)
997 mc->idx = mem_gc_realloc_n_typed_zeroed(interp, mc->idx,
998 type + 1, mc->mc_size, Meth_cache_entry **);
999 else
1000 mc->idx = mem_gc_allocate_n_zeroed_typed(interp, type + 1,
1001 Meth_cache_entry **);
1003 mc->mc_size = type + 1;
1006 if (! mc->idx[type])
1007 mc->idx[type] = mem_gc_allocate_n_zeroed_typed(interp,
1008 TBL_SIZE, Meth_cache_entry *);
1010 e = mc->idx[type][bits];
1012 while (e && e->strstart != Buffer_bufstart(method_name))
1013 e = e->next;
1015 if (!e) {
1016 /* when here no or no correct entry was at [bits] */
1017 /* Use zeroed allocation because find_method_direct can trigger GC */
1018 e = mem_gc_allocate_zeroed_typed(interp, Meth_cache_entry);
1020 mc->idx[type][bits] = e;
1022 e->pmc = Parrot_find_method_direct(interp, _class, method_name);
1023 e->next = NULL;
1024 e->strstart = Buffer_bufstart(method_name);
1027 return e->pmc;
1029 #endif
1035 =item C<static PMC* C3_merge(PARROT_INTERP, PMC *merge_list)>
1037 Merge together the MRO of the items in the list.
1039 =cut
1043 PARROT_WARN_UNUSED_RESULT
1044 PARROT_CAN_RETURN_NULL
1045 static PMC*
1046 C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
1048 ASSERT_ARGS(C3_merge)
1049 PMC *accepted = PMCNULL;
1050 PMC *result = PMCNULL;
1051 const int list_count = VTABLE_elements(interp, merge_list);
1052 int cand_count = 0;
1053 int i;
1055 /* Try and find something appropriate to add to the MRO - basically, the
1056 * first list head that is not in the tail of all the other lists. */
1057 for (i = 0; i < list_count; ++i) {
1058 PMC * const cand_list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1060 PMC *cand_class;
1061 int reject = 0;
1062 int j;
1064 if (VTABLE_elements(interp, cand_list) == 0)
1065 continue;
1067 cand_class = VTABLE_get_pmc_keyed_int(interp, cand_list, 0);
1068 ++cand_count;
1070 for (j = 0; j < list_count; ++j) {
1071 /* Skip the current list. */
1072 if (j != i) {
1073 /* Is it in the tail? If so, reject. */
1074 PMC * const check_list =
1075 VTABLE_get_pmc_keyed_int(interp, merge_list, j);
1077 const int check_length = VTABLE_elements(interp, check_list);
1078 int k;
1080 for (k = 1; k < check_length; ++k) {
1081 if (VTABLE_get_pmc_keyed_int(interp, check_list, k) ==
1082 cand_class) {
1083 reject = 1;
1084 break;
1090 /* If we didn't reject it, this candidate will do. */
1091 if (!reject) {
1092 accepted = cand_class;
1093 break;
1097 /* If we never found any candidates, return an empty list. */
1098 if (cand_count == 0)
1099 return Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1101 /* If we didn't find anything to accept, error. */
1102 if (PMC_IS_NULL(accepted))
1103 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
1104 "Could not build C3 linearization: ambiguous hierarchy");
1106 /* Otherwise, remove what was accepted from the merge lists. */
1107 for (i = 0; i < list_count; ++i) {
1109 PMC * const list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1110 const INTVAL sublist_count = VTABLE_elements(interp, list);
1111 INTVAL j;
1113 for (j = 0; j < sublist_count; ++j) {
1114 if (VTABLE_get_pmc_keyed_int(interp, list, j) == accepted) {
1115 VTABLE_delete_keyed_int(interp, list, j);
1116 break;
1121 /* Need to merge what remains of the list, then put what was accepted on
1122 * the start of the list, and we're done. */
1123 result = C3_merge(interp, merge_list);
1124 VTABLE_unshift_pmc(interp, result, accepted);
1126 return result;
1132 =item C<PMC* Parrot_ComputeMRO_C3(PARROT_INTERP, PMC *_class)>
1134 Computes the C3 linearization for the given class. C3 is an algorithm to
1135 compute the method resolution order (MRO) of a class that is inheriting
1136 from multiple parent classes (multiple inheritance). C3 was first described
1137 by Barrett et al at:
1139 F<http://192.220.96.201/dylan/linearization-oopsla96.html>
1141 =cut
1145 PARROT_EXPORT
1146 PARROT_WARN_UNUSED_RESULT
1147 PARROT_CAN_RETURN_NULL
1148 PMC*
1149 Parrot_ComputeMRO_C3(PARROT_INTERP, ARGIN(PMC *_class))
1151 ASSERT_ARGS(Parrot_ComputeMRO_C3)
1153 PMC * const immediate_parents = VTABLE_inspect_str(interp, _class, CONST_STRING(interp, "parents"));
1154 PMC *merge_list;
1155 PMC *result;
1157 INTVAL i;
1158 INTVAL parent_count;
1160 /* Now get immediate parents list. */
1161 if (PMC_IS_NULL(immediate_parents))
1162 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_METHOD_NOT_FOUND,
1163 "Failed to get parents list from class!");
1165 parent_count = VTABLE_elements(interp, immediate_parents);
1167 if (parent_count == 0) {
1168 /* No parents - MRO just contains this class. */
1169 result = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1170 VTABLE_push_pmc(interp, result, _class);
1171 return result;
1174 /* Otherwise, need to do merge. For that, need linearizations of all of
1175 * our parents added to the merge list. */
1176 merge_list = PMCNULL;
1177 for (i = 0; i < parent_count; ++i) {
1178 PMC * const lin = Parrot_ComputeMRO_C3(interp,
1179 VTABLE_get_pmc_keyed_int(interp, immediate_parents, i));
1181 if (PMC_IS_NULL(lin))
1182 return PMCNULL;
1184 /* instantiated lazily */
1185 if (PMC_IS_NULL(merge_list))
1186 merge_list = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1188 VTABLE_push_pmc(interp, merge_list, lin);
1191 /* Finally, need list of direct parents on the end of the merge list, then
1192 * we can merge. */
1193 VTABLE_push_pmc(interp, merge_list, immediate_parents);
1194 result = C3_merge(interp, merge_list);
1196 if (PMC_IS_NULL(result))
1197 return PMCNULL;
1199 /* Merged result needs this class on the start, and then we're done. */
1200 VTABLE_unshift_pmc(interp, result, _class);
1202 return result;
1208 =item C<void Parrot_ComposeRole(PARROT_INTERP, PMC *role, PMC *exclude, int
1209 got_exclude, PMC *alias, int got_alias, PMC *methods_hash, PMC *roles_list)>
1211 Used by the Class and Object PMCs internally to compose a role into either of
1212 them. The C<role> parameter is the role that we are composing into the class
1213 or role. C<methods_hash> is the hash of method names to invokable PMCs that
1214 contains the methods the class or role has. C<roles_list> is the list of roles
1215 the the class or method does.
1217 The C<role> parameter is only dealt with by its external interface. Whether
1218 this routine is usable by any other object system implemented in Parrot very
1219 much depends on how closely the role composition semantics they want are to
1220 the default implementation.
1222 =cut
1226 PARROT_EXPORT
1227 void
1228 Parrot_ComposeRole(PARROT_INTERP, ARGIN(PMC *role),
1229 ARGIN(PMC *exclude), int got_exclude,
1230 ARGIN(PMC *alias), int got_alias,
1231 ARGIN(PMC *methods_hash), ARGIN(PMC *roles_list))
1233 ASSERT_ARGS(Parrot_ComposeRole)
1234 PMC *methods;
1235 PMC *methods_iter;
1236 PMC *roles_of_role;
1237 PMC *proposed_add_methods;
1239 INTVAL roles_of_role_count;
1240 INTVAL i;
1242 /* Check we have not already composed the role; if so, just ignore it. */
1243 INTVAL roles_count = VTABLE_elements(interp, roles_list);
1245 for (i = 0; i < roles_count; ++i)
1246 if (VTABLE_get_pmc_keyed_int(interp, roles_list, i) == role)
1247 return;
1249 /* Get the methods from the role. */
1250 Parrot_pcc_invoke_method_from_c_args(interp, role, CONST_STRING(interp, "methods"), "->P", &methods);
1252 if (PMC_IS_NULL(methods))
1253 return;
1255 /* We need to check for conflicts before we do the composition. We
1256 * put each method that would be OK to add into a proposal list, and
1257 * bail out right away if we find a problem. */
1258 proposed_add_methods = Parrot_pmc_new(interp, enum_class_Hash);
1259 methods_iter = VTABLE_get_iter(interp, methods);
1261 while (VTABLE_get_bool(interp, methods_iter)) {
1262 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1263 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp, methods,
1264 method_name);
1266 /* Need to find the name we'll check for a conflict on. */
1267 int excluded = 0;
1269 /* Check if it's in the exclude list. */
1270 if (got_exclude) {
1271 const int exclude_count = VTABLE_elements(interp, exclude);
1273 for (i = 0; i < exclude_count; ++i) {
1274 const STRING * const check =
1275 VTABLE_get_string_keyed_int(interp, exclude, i);
1277 if (Parrot_str_equal(interp, check, method_name)) {
1278 excluded = 1;
1279 break;
1284 /* If we weren't excluded... */
1285 if (!excluded) {
1286 /* Is there a method with this name already in the class? */
1288 if (VTABLE_exists_keyed_str(interp, methods_hash, method_name)) {
1289 /* Conflicts with something already in the class, unless it's a
1290 * multi-method. */
1291 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, method_name);
1292 if (PMC_IS_NULL(cur_entry) || !VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1293 Parrot_ex_throw_from_c_args(interp, NULL,
1294 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1295 "A conflict occurred during role composition "
1296 "due to method '%S'.", method_name);
1299 /* What about a conflict with ourslef? */
1300 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1301 method_name))
1302 /* Something very weird is going on. */
1303 Parrot_ex_throw_from_c_args(interp, NULL,
1304 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1305 "A conflict occurred during role composition;"
1306 " the method '%S' from the role managed to conflict "
1307 "with itself somehow.", method_name);
1309 /* If we got here, no conflicts! Add method to the "to compose"
1310 * list. */
1311 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1312 method_name, cur_method);
1315 /* Now see if we've got an alias. */
1316 if (got_alias && VTABLE_exists_keyed_str(interp, alias, method_name)) {
1317 /* Got one. Get name to alias it to. */
1318 STRING * const alias_name = VTABLE_get_string_keyed_str(interp,
1319 alias, method_name);
1321 /* Is there a method with this name already in the class? If it's
1322 * not a multi-method, error. */
1323 if (VTABLE_exists_keyed_str(interp, methods_hash, alias_name)) {
1324 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, alias_name);
1325 if (PMC_IS_NULL(cur_entry) || !VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1326 /* Conflicts with something already in the class. */
1327 Parrot_ex_throw_from_c_args(interp, NULL,
1328 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1329 "A conflict occurred during role composition"
1330 " due to the aliasing of '%S' to '%S'.",
1331 method_name, alias_name);
1334 /* What about a conflict with ourslef? */
1335 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1336 alias_name))
1337 Parrot_ex_throw_from_c_args(interp, NULL,
1338 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1339 "A conflict occurred during role composition"
1340 " due to the aliasing of '%S' to '%S' (role already has"
1341 " a method '%S').", method_name, alias_name, alias_name);
1343 /* If we get here, no conflicts! Add method to the "to compose"
1344 * list with its alias. */
1345 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1346 alias_name, cur_method);
1350 /* If we get here, we detected no conflicts. Go ahead and compose the
1351 * methods. */
1352 methods_iter = VTABLE_get_iter(interp, proposed_add_methods);
1354 while (VTABLE_get_bool(interp, methods_iter)) {
1355 /* Get current method and its name. */
1356 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1357 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp,
1358 proposed_add_methods, method_name);
1360 /* Add it to the methods of the class. */
1361 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, method_name);
1362 if (VTABLE_isa(interp, cur_method, CONST_STRING(interp, "MultiSub"))) {
1363 /* The thing we're adding is a multi-sub, but is the thing in the
1364 * class already a multi-sub? */
1365 if (!PMC_IS_NULL(cur_entry) && VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub"))) {
1366 /* Class already has a multi-sub; need to merge our methods into it. */
1367 const INTVAL num_subs = VTABLE_elements(interp, cur_method);
1368 INTVAL j;
1369 for (j = 0; j < num_subs; ++j)
1370 VTABLE_push_pmc(interp, cur_entry, VTABLE_get_pmc_keyed_int(interp,
1371 cur_method, j));
1373 else {
1374 /* It's not, and we didn't conflict so must be no entry. Just stick it in. */
1375 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1378 else {
1379 /* Are we adding into a multi-sub? */
1380 if (!PMC_IS_NULL(cur_entry) && VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1381 VTABLE_push_pmc(interp, cur_entry, cur_method);
1382 else
1383 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1387 /* Add this role to the roles list. */
1388 VTABLE_push_pmc(interp, roles_list, role);
1389 ++roles_count;
1391 /* As a result of composing this role, we will also now do the roles
1392 * that it did itself. Note that we already have the correct methods
1393 * as roles "flatten" the methods they get from other roles into their
1394 * own method list. */
1395 Parrot_pcc_invoke_method_from_c_args(interp, role, CONST_STRING(interp, "roles"), "->P", &roles_of_role);
1396 roles_of_role_count = VTABLE_elements(interp, roles_of_role);
1398 for (i = 0; i < roles_of_role_count; ++i) {
1399 /* Only add if we don't already have it in the list. */
1400 PMC * const cur_role = VTABLE_get_pmc_keyed_int(interp,
1401 roles_of_role, i);
1402 INTVAL j;
1404 for (j = 0; j < roles_count; ++j) {
1405 if (VTABLE_get_pmc_keyed_int(interp, roles_list, j) == cur_role) {
1406 /* We ain't be havin' it. */
1407 VTABLE_push_pmc(interp, roles_list, cur_role);
1416 =back
1418 =head1 SEE ALSO
1420 F<include/parrot/oo.h>, F<include/parrot/oo_private.h>,
1421 F<docs/pdds/pdd15_objects.pod>.
1423 =cut
1428 * Local variables:
1429 * c-file-style: "parrot"
1430 * End:
1431 * vim: expandtab shiftwidth=4: