remove deprecation notice for TT #449
[parrot.git] / src / oo.c
blob6751e3fe8448e98bc625a97816d3bf3d23e34b9e
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_WARN_UNUSED_RESULT
54 PARROT_CAN_RETURN_NULL
55 static PMC * find_method_direct_1(PARROT_INTERP,
56 ARGIN(PMC *_class),
57 ARGIN(STRING *method_name))
58 __attribute__nonnull__(1)
59 __attribute__nonnull__(2)
60 __attribute__nonnull__(3);
62 PARROT_INLINE
63 PARROT_CANNOT_RETURN_NULL
64 PARROT_WARN_UNUSED_RESULT
65 static PMC * get_pmc_proxy(PARROT_INTERP, INTVAL type)
66 __attribute__nonnull__(1);
68 static void invalidate_all_caches(PARROT_INTERP)
69 __attribute__nonnull__(1);
71 static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)
72 __attribute__nonnull__(1);
74 #define ASSERT_ARGS_C3_merge __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
75 PARROT_ASSERT_ARG(interp) \
76 , PARROT_ASSERT_ARG(merge_list))
77 #define ASSERT_ARGS_debug_trace_find_meth __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
78 PARROT_ASSERT_ARG(interp) \
79 , PARROT_ASSERT_ARG(_class) \
80 , PARROT_ASSERT_ARG(name))
81 #define ASSERT_ARGS_fail_if_type_exists __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
82 PARROT_ASSERT_ARG(interp) \
83 , PARROT_ASSERT_ARG(name))
84 #define ASSERT_ARGS_find_method_direct_1 __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
85 PARROT_ASSERT_ARG(interp) \
86 , PARROT_ASSERT_ARG(_class) \
87 , PARROT_ASSERT_ARG(method_name))
88 #define ASSERT_ARGS_get_pmc_proxy __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
89 PARROT_ASSERT_ARG(interp))
90 #define ASSERT_ARGS_invalidate_all_caches __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
91 PARROT_ASSERT_ARG(interp))
92 #define ASSERT_ARGS_invalidate_type_caches __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
93 PARROT_ASSERT_ARG(interp))
94 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
95 /* HEADERIZER END: static */
99 =item C<void Parrot_oo_extract_methods_from_namespace(PARROT_INTERP, PMC *self,
100 PMC *ns)>
102 Extract methods and vtable overrides from the given namespace and insert them
103 into the class.
105 =cut
109 void
110 Parrot_oo_extract_methods_from_namespace(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *ns))
112 ASSERT_ARGS(Parrot_oo_extract_methods_from_namespace)
113 PMC *methods, *vtable_overrides;
115 /* Pull in methods from the namespace, if any. */
116 if (PMC_IS_NULL(ns))
117 return;
119 /* Import any methods. */
120 Parrot_pcc_invoke_method_from_c_args(interp, ns, CONST_STRING(interp, "get_associated_methods"), "->P", &methods);
122 if (!PMC_IS_NULL(methods)) {
123 PMC * const iter = VTABLE_get_iter(interp, methods);
125 while (VTABLE_get_bool(interp, iter)) {
126 STRING * const meth_name = VTABLE_shift_string(interp, iter);
127 PMC * const meth_sub = VTABLE_get_pmc_keyed_str(interp, methods,
128 meth_name);
129 VTABLE_add_method(interp, self, meth_name, meth_sub);
133 /* Import any vtables. */
134 Parrot_pcc_invoke_method_from_c_args(interp, ns, CONST_STRING(interp, "get_associated_vtable_methods"), "->P", &vtable_overrides);
136 if (!PMC_IS_NULL(vtable_overrides)) {
137 PMC * const iter = VTABLE_get_iter(interp, vtable_overrides);
138 while (VTABLE_get_bool(interp, iter)) {
139 STRING * const vtable_index_str = VTABLE_shift_string(interp, iter);
140 PMC * const vtable_sub = VTABLE_get_pmc_keyed_str(interp,
141 vtable_overrides, vtable_index_str);
143 /* Look up the name of the vtable function from the index. */
144 const INTVAL vtable_index = Parrot_str_to_int(interp, vtable_index_str);
145 const char * const meth_c = Parrot_vtable_slot_names[vtable_index];
146 STRING * const vtable_name = Parrot_str_new(interp, meth_c, 0);
147 VTABLE_add_vtable_override(interp, self, vtable_name, vtable_sub);
155 =item C<PMC * Parrot_oo_get_class(PARROT_INTERP, PMC *key)>
157 Lookup a class object from a namespace, string, or key PMC.
159 TODO: This function is terribly inefficient. It needs to be refactored in a
160 major way
162 =cut
166 PARROT_EXPORT
167 PARROT_CAN_RETURN_NULL
168 PARROT_WARN_UNUSED_RESULT
169 PMC *
170 Parrot_oo_get_class(PARROT_INTERP, ARGIN(PMC *key))
172 ASSERT_ARGS(Parrot_oo_get_class)
173 PMC *classobj = PMCNULL;
175 if (PMC_IS_NULL(key))
176 return PMCNULL;
178 if (PObj_is_class_TEST(key))
179 classobj = key;
180 else {
181 /* Fast select of behavior based on type of the lookup key */
182 switch (key->vtable->base_type) {
183 case enum_class_NameSpace:
184 classobj = VTABLE_get_class(interp, key);
185 break;
186 case enum_class_String:
187 case enum_class_Key:
188 case enum_class_ResizableStringArray:
190 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp,
191 interp->HLL_namespace,
192 Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
193 PMC * const ns = Parrot_get_namespace_keyed(interp,
194 hll_ns, key);
196 if (!PMC_IS_NULL(ns))
197 classobj = VTABLE_get_class(interp, ns);
199 default:
200 break;
204 /* If the PMCProxy doesn't exist yet for the given key, we look up the
205 type ID here and create a new one */
206 if (PMC_IS_NULL(classobj)) {
207 INTVAL type;
208 const INTVAL base_type = key->vtable->base_type;
210 /* This is a hack! All PMCs should be able to be handled through
211 a single codepath, and all of them should be able to avoid
212 stringification because it's so imprecise. */
213 if (base_type == enum_class_Key
214 || base_type == enum_class_ResizableStringArray
215 || base_type == enum_class_String)
216 type = Parrot_pmc_get_type(interp, key);
217 else
218 type = Parrot_pmc_get_type_str(interp, VTABLE_get_string(interp, key));
220 classobj = get_pmc_proxy(interp, type);
223 return classobj;
228 =item C<PMC * Parrot_oo_clone_object(PARROT_INTERP, PMC *pmc, PMC *dest)>
230 Clone an Object PMC. If an existing PMC C<dest> is provided, reuse that
231 PMC to store copies of the data. Otherwise, create a new PMC and populate
232 that with the data.
234 =cut
238 PARROT_CANNOT_RETURN_NULL
239 PMC *
240 Parrot_oo_clone_object(PARROT_INTERP, ARGIN(PMC *pmc), ARGMOD_NULLOK(PMC *dest))
242 ASSERT_ARGS(Parrot_oo_clone_object)
243 Parrot_Object_attributes *obj = PARROT_OBJECT(pmc);
244 Parrot_Object_attributes *cloned_guts;
245 Parrot_Class_attributes *_class;
246 PMC *cloned;
247 INTVAL num_classes;
248 INTVAL i, num_attrs;
250 if (!PMC_IS_NULL(dest)) {
251 cloned = dest;
253 else {
254 cloned = Parrot_pmc_new_noinit(interp, enum_class_Object);
257 _class = PARROT_CLASS(obj->_class);
258 PARROT_ASSERT(_class);
259 num_classes = VTABLE_elements(interp, _class->all_parents);
261 /* Set custom GC mark and destroy on the object. */
262 PObj_custom_mark_SET(cloned);
263 PObj_custom_destroy_SET(cloned);
265 /* Flag that it is an object */
266 PObj_is_object_SET(cloned);
268 /* Now clone attributes list.class. */
269 cloned_guts = (Parrot_Object_attributes *) PMC_data(cloned);
270 cloned_guts->_class = obj->_class;
271 cloned_guts->attrib_store = VTABLE_clone(interp, obj->attrib_store);
272 num_attrs = VTABLE_elements(interp, cloned_guts->attrib_store);
273 for (i = 0; i < num_attrs; ++i) {
274 PMC * const to_clone = VTABLE_get_pmc_keyed_int(interp, cloned_guts->attrib_store, i);
275 if (!PMC_IS_NULL(to_clone)) {
276 VTABLE_set_pmc_keyed_int(interp, cloned_guts->attrib_store, i,
277 VTABLE_clone(interp, to_clone));
281 /* Some of the attributes may have been the PMCs providing storage for any
282 * PMCs we inherited from; also need to clone those. */
283 if (CLASS_has_alien_parents_TEST(obj->_class)) {
284 int j;
285 /* Locate any PMC parents. */
286 for (j = 0; j < num_classes; ++j) {
287 PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp, _class->all_parents, j);
288 if (cur_class->vtable->base_type == enum_class_PMCProxy) {
289 /* Clone this PMC too. */
290 STRING * const proxy = CONST_STRING(interp, "proxy");
291 VTABLE_set_attr_keyed(interp, cloned, cur_class, proxy,
292 VTABLE_clone(interp,
293 VTABLE_get_attr_keyed(interp, cloned, cur_class, proxy)));
298 /* And we have ourselves a clone. */
299 return cloned;
304 =item C<static PMC * get_pmc_proxy(PARROT_INTERP, INTVAL type)>
306 Get the PMC proxy for a PMC with the given type, creating it if does not exist.
307 If type is not a valid type, return PMCNULL. This code assumes that
308 all PMCProxy objects live in the 'parrot' HLL namespace -- if/when
309 we allow PMC types to exist in other HLL namespaces, this code will
310 need to be updated.
312 For internal use only.
314 =cut
318 PARROT_INLINE
319 PARROT_CANNOT_RETURN_NULL
320 PARROT_WARN_UNUSED_RESULT
321 static PMC *
322 get_pmc_proxy(PARROT_INTERP, INTVAL type)
324 ASSERT_ARGS(get_pmc_proxy)
325 PMC * type_class;
327 /* Check if not a PMC or invalid type number */
328 if (type > interp->n_vtable_max || type <= 0)
329 return PMCNULL;
331 type_class = interp->vtables[type]->pmc_class;
332 if (type != enum_class_Class
333 && type_class->vtable->base_type == enum_class_Class) {
334 return type_class;
336 else {
337 PMC * const parrot_hll = Parrot_get_namespace_keyed_str(interp, interp->root_namespace, CONST_STRING(interp, "parrot"));
338 PMC * const pmc_ns =
339 Parrot_make_namespace_keyed_str(interp, parrot_hll,
340 interp->vtables[type]->whoami);
341 PMC * proxy = VTABLE_get_class(interp, pmc_ns);
343 /* Create proxy if not found */
344 if (PMC_IS_NULL(proxy)) {
345 proxy = Parrot_pmc_new_init_int(interp, enum_class_PMCProxy, type);
346 Parrot_pcc_invoke_method_from_c_args(interp, pmc_ns, CONST_STRING(interp, "set_class"), "P->", proxy);
348 return proxy;
354 =item C<PMC * Parrot_oo_get_class_str(PARROT_INTERP, STRING *name)>
356 Lookup a class object from the string C<name>. If the metaobject is found,
357 return it. Otherwise, create a new PMCProxy for the type ID number.
359 =cut
363 PARROT_EXPORT
364 PARROT_CAN_RETURN_NULL
365 PARROT_WARN_UNUSED_RESULT
366 PMC *
367 Parrot_oo_get_class_str(PARROT_INTERP, ARGIN_NULLOK(STRING *name))
369 ASSERT_ARGS(Parrot_oo_get_class_str)
371 if (STRING_IS_NULL(name))
372 return PMCNULL;
373 else {
375 /* First check in current HLL namespace */
376 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp, interp->HLL_namespace,
377 Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
378 PMC * const ns = Parrot_get_namespace_keyed_str(interp, hll_ns, name);
379 PMC * const _class = PMC_IS_NULL(ns)
380 ? PMCNULL : VTABLE_get_class(interp, ns);
382 /* If not found, check for a PMC */
383 if (PMC_IS_NULL(_class))
384 return get_pmc_proxy(interp, Parrot_pmc_get_type_str(interp, name));
385 else
386 return _class;
393 =item C<PMC * Parrot_oo_newclass_from_str(PARROT_INTERP, STRING *name)>
395 Create a new Class PMC for a new type of the given C<name>.
397 =cut
401 PARROT_CAN_RETURN_NULL
402 PARROT_WARN_UNUSED_RESULT
403 PMC *
404 Parrot_oo_newclass_from_str(PARROT_INTERP, ARGIN(STRING *name))
406 ASSERT_ARGS(Parrot_oo_newclass_from_str)
407 PMC * const namearg = Parrot_pmc_new(interp, enum_class_String);
408 PMC * const namehash = Parrot_pmc_new(interp, enum_class_Hash);
409 PMC *classobj;
411 VTABLE_set_string_native(interp, namearg, name);
412 VTABLE_set_pmc_keyed_str(interp, namehash, CONST_STRING(interp, "name"), namearg);
414 classobj = Parrot_pmc_new_init(interp, enum_class_Class, namehash);
416 PARROT_ASSERT(classobj);
417 return classobj;
423 =item C<PMC * Parrot_oo_find_vtable_override_for_class(PARROT_INTERP, PMC
424 *classobj, STRING *name)>
426 Find the vtable override with the specified C<name> in the given C<classobj>
427 metaobject.
429 =cut
433 PARROT_EXPORT
434 PARROT_CAN_RETURN_NULL
435 PARROT_WARN_UNUSED_RESULT
436 PMC *
437 Parrot_oo_find_vtable_override_for_class(PARROT_INTERP,
438 ARGIN(PMC *classobj), ARGIN(STRING *name))
440 ASSERT_ARGS(Parrot_oo_find_vtable_override_for_class)
441 const Parrot_Class_attributes * const class_info = PARROT_CLASS(classobj);;
442 PARROT_ASSERT(PObj_is_class_TEST(classobj));
444 return VTABLE_get_pmc_keyed_str(interp, class_info->vtable_overrides, name);
450 =item C<PMC * Parrot_oo_find_vtable_override(PARROT_INTERP, PMC *classobj,
451 STRING *name)>
453 Lookup a vtable override in a class, including any vtable overrides inherited
454 from parents.
456 =cut
460 PARROT_EXPORT
461 PARROT_CAN_RETURN_NULL
462 PARROT_WARN_UNUSED_RESULT
463 PMC *
464 Parrot_oo_find_vtable_override(PARROT_INTERP,
465 ARGIN(PMC *classobj), ARGIN(STRING *name))
467 ASSERT_ARGS(Parrot_oo_find_vtable_override)
468 Parrot_Class_attributes * const _class = PARROT_CLASS(classobj);
469 PMC *result =
470 VTABLE_get_pmc_keyed_str(interp, _class->parent_overrides, name);
472 if (PMC_IS_NULL(result)) {
473 /* Walk and search for the vtable. */
474 const INTVAL num_classes = VTABLE_elements(interp, _class->all_parents);
475 INTVAL i;
477 for (i = 0; i < num_classes; ++i) {
478 /* Get the class. */
479 PMC * const cur_class =
480 VTABLE_get_pmc_keyed_int(interp, _class->all_parents, i);
482 result = Parrot_oo_find_vtable_override_for_class(interp,
483 cur_class, name);
485 if (!PMC_IS_NULL(result))
486 break;
488 if (PMC_IS_NULL(result))
489 result = Parrot_pmc_new(interp, enum_class_Undef);
490 VTABLE_set_pmc_keyed_str(interp, _class->parent_overrides, name, result);
492 if (result->vtable->base_type == enum_class_Undef)
493 result = PMCNULL;
494 return result;
500 =item C<INTVAL Parrot_get_vtable_index(PARROT_INTERP, const STRING *name)>
502 Return index if C<name> is a valid vtable slot name.
504 =cut
508 PARROT_EXPORT
509 INTVAL
510 Parrot_get_vtable_index(PARROT_INTERP, ARGIN(const STRING *name))
512 ASSERT_ARGS(Parrot_get_vtable_index)
513 char * const name_c = Parrot_str_to_cstring(interp, name);
515 /* some of the first "slots" don't have names. skip 'em. */
516 INTVAL low = PARROT_VTABLE_LOW;
517 INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW;
519 while (low < high) {
520 const INTVAL mid = (low + high) / 2;
521 const char * const meth_c = Parrot_vtable_slot_names[mid];
523 const INTVAL cmp = strcmp(name_c, meth_c);
525 if (cmp == 0) {
526 Parrot_str_free_cstring(name_c);
527 return mid;
529 else if (cmp > 0)
530 low = mid + 1;
531 else
532 high = mid;
535 Parrot_str_free_cstring(name_c);
537 return -1;
543 =item C<const char * Parrot_get_vtable_name(PARROT_INTERP, INTVAL idx)>
545 Return the method name at the specified index in the vtable slot array.
546 Use this function when you cannot access Parrot_vtable_slot_names directly.
548 =cut
552 PARROT_EXPORT
553 PARROT_PURE_FUNCTION
554 PARROT_CAN_RETURN_NULL
555 const char *
556 Parrot_get_vtable_name(SHIM_INTERP, INTVAL idx)
558 ASSERT_ARGS(Parrot_get_vtable_name)
560 const INTVAL low = PARROT_VTABLE_LOW;
561 const INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW;
563 PARROT_ASSERT(idx > 0);
565 if (idx < low || idx > high) {
566 return NULL;
569 return Parrot_vtable_slot_names[idx];
575 =item C<static INTVAL fail_if_type_exists(PARROT_INTERP, PMC *name)>
577 This function throws an exception if a PMC or class with the same name *
578 already exists in the global type registry. The global type registry
579 will go away eventually, but this allows the new object metamodel to
580 interact with the old one until it does.
582 =cut
586 static INTVAL
587 fail_if_type_exists(PARROT_INTERP, ARGIN(PMC *name))
589 ASSERT_ARGS(fail_if_type_exists)
590 PMC * const value = (PMC *)VTABLE_get_pointer_keyed(interp, interp->class_hash, name);
592 if (PMC_IS_NULL(value))
593 return 0;
595 switch (VTABLE_type(interp, value)) {
596 case enum_class_NameSpace:
597 return 0;
598 break;
599 case enum_class_Integer:
601 const INTVAL type = VTABLE_get_integer(interp, value);
602 if (type < enum_type_undef) {
603 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
604 "native type with name '%s' already exists - "
605 "can't register Class", data_types[type].name);
607 return type;
609 break;
610 default:
611 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERP_ERROR,
612 "Unrecognized class name PMC type");
613 break;
620 =item C<INTVAL Parrot_oo_register_type(PARROT_INTERP, PMC *name, PMC
621 *_namespace)>
623 This function registers a type in the global registry, first checking if it
624 already exists. The global type registry will go away eventually, but this
625 allows the new object metamodel to interact with the old one until it does.
627 =cut
631 PARROT_WARN_UNUSED_RESULT
632 INTVAL
633 Parrot_oo_register_type(PARROT_INTERP, ARGIN(PMC *name), ARGIN(PMC *_namespace))
635 ASSERT_ARGS(Parrot_oo_register_type)
636 INTVAL type;
637 const INTVAL typeid_exists = fail_if_type_exists(interp, name);
639 PMC * const classobj = VTABLE_get_class(interp, _namespace);
640 if (!PMC_IS_NULL(classobj)) {
641 STRING * const classname = VTABLE_get_string(interp, _namespace);
642 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
643 "Class %Ss already registered!\n",
644 Parrot_str_escape(interp, classname));
647 /* Type doesn't exist, so go ahead and register it. Lock interpreter so
648 * pt_shared_fixup() can safely do a type lookup. */
649 LOCK_INTERPRETER(interp);
651 type = Parrot_pmc_get_new_vtable_index(interp);
654 if (!typeid_exists) {
655 /* set entry in name->type hash */
656 PMC * const classname_hash = interp->class_hash;
657 PMC * const item = Parrot_pmc_new_init_int(interp,
658 enum_class_Integer, type);
659 VTABLE_set_pmc_keyed(interp, classname_hash, name, item);
662 UNLOCK_INTERPRETER(interp);
664 return type;
669 =item C<void mark_object_cache(PARROT_INTERP)>
671 Marks all PMCs in the object method cache as live. This shouldn't strictly be
672 necessary, as they're likely all reachable from namespaces and classes, but
673 it's unlikely to hurt anything except mark phase performance.
675 =cut
679 #define TBL_SIZE_MASK 0x1ff /* x bits 2..10 */
680 #define TBL_SIZE (1 + TBL_SIZE_MASK)
682 void
683 mark_object_cache(PARROT_INTERP)
685 ASSERT_ARGS(mark_object_cache)
686 Caches * const mc = interp->caches;
687 UINTVAL type, entry;
689 if (!mc)
690 return;
692 for (type = 0; type < mc->mc_size; ++type) {
693 if (!mc->idx[type])
694 continue;
696 for (entry = 0; entry < TBL_SIZE; ++entry) {
697 Meth_cache_entry *e = mc->idx[type][entry];
698 while (e) {
699 Parrot_gc_mark_PMC_alive(interp, e->pmc);
700 e = e->next;
709 =item C<void init_object_cache(PARROT_INTERP)>
711 Allocate memory for object cache.
713 =cut
717 void
718 init_object_cache(PARROT_INTERP)
720 ASSERT_ARGS(init_object_cache)
721 Caches * const mc = interp->caches = mem_gc_allocate_zeroed_typed(interp, Caches);
722 mc->idx = NULL;
728 =item C<void destroy_object_cache(PARROT_INTERP)>
730 Destroy the object cache. Loop over all caches and invalidate them. Then
731 free the caches back to the OS.
733 =cut
737 void
738 destroy_object_cache(PARROT_INTERP)
740 ASSERT_ARGS(destroy_object_cache)
741 UINTVAL i;
742 Caches * const mc = interp->caches;
744 /* mc->idx[type][bits] = e; */
745 for (i = 0; i < mc->mc_size; ++i) {
746 if (mc->idx[i])
747 invalidate_type_caches(interp, i);
750 mem_gc_free(interp, mc->idx);
751 mem_gc_free(interp, mc);
757 =item C<static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)>
759 Invalidate the cache of the specified type. Free each entry and then free
760 the entire cache.
762 =cut
766 static void
767 invalidate_type_caches(PARROT_INTERP, UINTVAL type)
769 ASSERT_ARGS(invalidate_type_caches)
770 Caches * const mc = interp->caches;
771 INTVAL i;
773 if (!mc)
774 return;
776 /* is it a valid entry */
777 if (type >= mc->mc_size || !mc->idx[type])
778 return;
780 for (i = 0; i < TBL_SIZE; ++i) {
781 Meth_cache_entry *e = mc->idx[type][i];
782 while (e) {
783 Meth_cache_entry * const next = e->next;
784 mem_gc_free(interp, e);
785 e = next;
789 mem_gc_free(interp, mc->idx[type]);
790 mc->idx[type] = NULL;
796 =item C<static void invalidate_all_caches(PARROT_INTERP)>
798 Invalidate all caches by looping over each cache and calling
799 C<invalidate_type_caches> on them.
801 =cut
805 static void
806 invalidate_all_caches(PARROT_INTERP)
808 ASSERT_ARGS(invalidate_all_caches)
809 int i;
810 for (i = 1; i < interp->n_vtable_max; ++i)
811 invalidate_type_caches(interp, i);
817 =item C<void Parrot_invalidate_method_cache(PARROT_INTERP, STRING *_class)>
819 Clear method cache for the given class. If class is NULL, caches for
820 all classes are invalidated.
822 =cut
826 PARROT_EXPORT
827 void
828 Parrot_invalidate_method_cache(PARROT_INTERP, ARGIN_NULLOK(STRING *_class))
830 ASSERT_ARGS(Parrot_invalidate_method_cache)
831 INTVAL type;
833 /* during interp creation and NCI registration the class_hash
834 * isn't yet up */
835 if (!interp->class_hash)
836 return;
838 if (interp->resume_flag & RESUME_INITIAL)
839 return;
841 if (!_class) {
842 invalidate_all_caches(interp);
843 return;
846 type = Parrot_pmc_get_type_str(interp, _class);
848 if (type == 0)
849 invalidate_all_caches(interp);
850 else if (type > 0)
851 invalidate_type_caches(interp, (UINTVAL)type);
856 =item C<PMC * Parrot_find_method_direct(PARROT_INTERP, PMC *_class, STRING
857 *method_name)>
859 Find a method PMC for a named method, given the class PMC, current
860 interpreter, and name of the method. Don't use a possible method cache.
862 =cut
866 PARROT_EXPORT
867 PARROT_CAN_RETURN_NULL
868 PARROT_WARN_UNUSED_RESULT
869 PMC *
870 Parrot_find_method_direct(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
872 ASSERT_ARGS(Parrot_find_method_direct)
873 PMC * const found = find_method_direct_1(interp, _class, method_name);
875 if (!PMC_IS_NULL(found))
876 return found;
879 if (Parrot_str_equal(interp, method_name, CONST_STRING(interp, "__get_string")))
880 return find_method_direct_1(interp, _class, CONST_STRING(interp, "__get_repr"));
882 return PMCNULL;
888 =item C<PMC * Parrot_find_method_with_cache(PARROT_INTERP, PMC *_class, STRING
889 *method_name)>
891 Find a method PMC for a named method, given the class PMC, current
892 interp, and name of the method.
894 This routine should use the current scope's method cache, if there is
895 one. If not, it creates a new method cache. Or, rather, it will when
896 we've got that bit working. For now it unconditionally goes and looks up
897 the name in the global stash.
899 =cut
903 PARROT_EXPORT
904 PARROT_CAN_RETURN_NULL
905 PARROT_WARN_UNUSED_RESULT
906 PMC *
907 Parrot_find_method_with_cache(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
909 ASSERT_ARGS(Parrot_find_method_with_cache)
911 UINTVAL type, bits;
912 Caches *mc;
913 Meth_cache_entry *e;
915 PARROT_ASSERT(method_name != 0);
917 #if DISABLE_METH_CACHE
918 return Parrot_find_method_direct(interp, _class, method_name);
919 #else
921 if (! PObj_constant_TEST(method_name))
922 return Parrot_find_method_direct(interp, _class, method_name);
924 mc = interp->caches;
925 type = _class->vtable->base_type;
926 bits = (((UINTVAL) Buffer_bufstart(method_name)) >> 2) & TBL_SIZE_MASK;
928 if (type >= mc->mc_size) {
929 if (mc->idx) {
930 mc->idx = mem_gc_realloc_n_typed_zeroed(interp, mc->idx,
931 type + 1, mc->mc_size, Meth_cache_entry**);
933 else {
934 mc->idx = mem_gc_allocate_n_zeroed_typed(interp, type + 1, Meth_cache_entry**);
936 mc->mc_size = type + 1;
939 if (mc->idx[type] == NULL) {
940 mc->idx[type] = mem_gc_allocate_n_zeroed_typed(interp,
941 TBL_SIZE, Meth_cache_entry*);
944 e = mc->idx[type][bits];
946 while (e && e->strstart != Buffer_bufstart(method_name)) {
947 e = e->next;
950 if (!e) {
951 /* when here no or no correct entry was at [bits] */
952 /* Use zeroed allocation because find_method_direct can trigger GC */
953 e = mem_gc_allocate_zeroed_typed(interp, Meth_cache_entry);
955 mc->idx[type][bits] = e;
957 e->pmc = Parrot_find_method_direct(interp, _class, method_name);
958 e->next = NULL;
959 e->strstart = Buffer_bufstart(method_name);
962 return e->pmc;
964 #endif
970 =item C<static void debug_trace_find_meth(PARROT_INTERP, const PMC *_class,
971 const STRING *name, const PMC *sub)>
973 Print some information about the search for a sub.
975 =cut
979 #ifdef NDEBUG
980 # define TRACE_FM(i, c, m, sub)
981 #else
982 # define TRACE_FM(i, c, m, sub) \
983 debug_trace_find_meth((i), (c), (m), (sub))
985 static void
986 debug_trace_find_meth(PARROT_INTERP, ARGIN(const PMC *_class),
987 ARGIN(const STRING *name), ARGIN_NULLOK(const PMC *sub))
989 ASSERT_ARGS(debug_trace_find_meth)
990 STRING *class_name;
991 const char *result;
992 Interp *tracer;
994 if (!Interp_trace_TEST(interp, PARROT_TRACE_FIND_METH_FLAG))
995 return;
997 if (PObj_is_class_TEST(_class)) {
998 SLOTTYPE * const class_array = PMC_data_typed(_class, SLOTTYPE *);
999 PMC * const class_name_pmc = get_attrib_num(class_array, PCD_CLASS_NAME);
1000 class_name = VTABLE_get_string(interp, class_name_pmc);
1002 else
1003 class_name = _class->vtable->whoami;
1005 if (sub) {
1006 if (sub->vtable->base_type == enum_class_NCI)
1007 result = "NCI";
1008 else
1009 result = "Sub";
1011 else
1012 result = "no";
1014 tracer = (interp->pdb && interp->pdb->debugger) ?
1015 interp->pdb->debugger :
1016 interp;
1017 Parrot_io_eprintf(tracer, "# find_method class '%Ss' method '%Ss': %s\n",
1018 class_name, name, result);
1021 #endif
1026 =item C<static PMC * find_method_direct_1(PARROT_INTERP, PMC *_class, STRING
1027 *method_name)>
1029 Find the method with the given name in the specified class.
1031 =cut
1035 PARROT_WARN_UNUSED_RESULT
1036 PARROT_CAN_RETURN_NULL
1037 static PMC *
1038 find_method_direct_1(PARROT_INTERP, ARGIN(PMC *_class),
1039 ARGIN(STRING *method_name))
1041 ASSERT_ARGS(find_method_direct_1)
1042 INTVAL i;
1044 PMC * const mro = _class->vtable->mro;
1045 const INTVAL n = VTABLE_elements(interp, mro);
1046 STRING * const methods_str = CONST_STRING(interp, "methods");
1047 STRING * const class_str = CONST_STRING(interp, "class");
1049 for (i = 0; i < n; ++i) {
1050 PMC * const _class = VTABLE_get_pmc_keyed_int(interp, mro, i);
1051 PMC * const ns = VTABLE_get_namespace(interp, _class);
1052 PMC * const class_obj = VTABLE_inspect_str(interp, ns, class_str);
1053 PMC *method = PMCNULL;
1054 PMC * method_hash;
1056 if (PMC_IS_NULL(class_obj))
1057 method_hash = VTABLE_inspect_str(interp, ns, methods_str);
1058 else
1059 method_hash = VTABLE_inspect_str(interp, class_obj, methods_str);
1061 if (!PMC_IS_NULL(method_hash))
1062 method = VTABLE_get_pmc_keyed_str(interp, method_hash, method_name);
1064 if (PMC_IS_NULL(method))
1065 method = VTABLE_get_pmc_keyed_str(interp, ns, method_name);
1067 TRACE_FM(interp, _class, method_name, method);
1069 if (!PMC_IS_NULL(method))
1070 return method;
1073 TRACE_FM(interp, _class, method_name, NULL);
1074 return PMCNULL;
1080 =item C<static PMC* C3_merge(PARROT_INTERP, PMC *merge_list)>
1082 Merge together the MRO of the items in the list.
1084 =cut
1088 PARROT_WARN_UNUSED_RESULT
1089 PARROT_CAN_RETURN_NULL
1090 static PMC*
1091 C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
1093 ASSERT_ARGS(C3_merge)
1094 PMC *accepted = PMCNULL;
1095 PMC *result = PMCNULL;
1096 const int list_count = VTABLE_elements(interp, merge_list);
1097 int cand_count = 0;
1098 int i;
1100 /* Try and find something appropriate to add to the MRO - basically, the
1101 * first list head that is not in the tail of all the other lists. */
1102 for (i = 0; i < list_count; ++i) {
1103 PMC * const cand_list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1105 PMC *cand_class;
1106 int reject = 0;
1107 int j;
1109 if (VTABLE_elements(interp, cand_list) == 0)
1110 continue;
1112 cand_class = VTABLE_get_pmc_keyed_int(interp, cand_list, 0);
1113 ++cand_count;
1115 for (j = 0; j < list_count; ++j) {
1116 /* Skip the current list. */
1117 if (j != i) {
1118 /* Is it in the tail? If so, reject. */
1119 PMC * const check_list =
1120 VTABLE_get_pmc_keyed_int(interp, merge_list, j);
1122 const int check_length = VTABLE_elements(interp, check_list);
1123 int k;
1125 for (k = 1; k < check_length; ++k) {
1126 if (VTABLE_get_pmc_keyed_int(interp, check_list, k) ==
1127 cand_class) {
1128 reject = 1;
1129 break;
1135 /* If we didn't reject it, this candidate will do. */
1136 if (!reject) {
1137 accepted = cand_class;
1138 break;
1142 /* If we never found any candidates, return an empty list. */
1143 if (cand_count == 0)
1144 return Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1146 /* If we didn't find anything to accept, error. */
1147 if (PMC_IS_NULL(accepted))
1148 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
1149 "Could not build C3 linearization: ambiguous hierarchy");
1151 /* Otherwise, remove what was accepted from the merge lists. */
1152 for (i = 0; i < list_count; ++i) {
1154 PMC * const list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1155 const INTVAL sublist_count = VTABLE_elements(interp, list);
1156 INTVAL j;
1158 for (j = 0; j < sublist_count; ++j) {
1159 if (VTABLE_get_pmc_keyed_int(interp, list, j) == accepted) {
1160 VTABLE_delete_keyed_int(interp, list, j);
1161 break;
1166 /* Need to merge what remains of the list, then put what was accepted on
1167 * the start of the list, and we're done. */
1168 result = C3_merge(interp, merge_list);
1169 VTABLE_unshift_pmc(interp, result, accepted);
1171 return result;
1177 =item C<PMC* Parrot_ComputeMRO_C3(PARROT_INTERP, PMC *_class)>
1179 Computes the C3 linearization for the given class. C3 is an algorithm to
1180 compute the method resolution order (MRO) of a class that is inheriting
1181 from multiple parent classes (multiple inheritance). C3 was first described
1182 by Barrett et al at:
1184 F<http://192.220.96.201/dylan/linearization-oopsla96.html>
1186 =cut
1190 PARROT_EXPORT
1191 PARROT_WARN_UNUSED_RESULT
1192 PARROT_CAN_RETURN_NULL
1193 PMC*
1194 Parrot_ComputeMRO_C3(PARROT_INTERP, ARGIN(PMC *_class))
1196 ASSERT_ARGS(Parrot_ComputeMRO_C3)
1198 PMC * const immediate_parents = VTABLE_inspect_str(interp, _class, CONST_STRING(interp, "parents"));
1199 PMC *merge_list;
1200 PMC *result;
1202 INTVAL i;
1203 INTVAL parent_count;
1205 /* Now get immediate parents list. */
1206 if (PMC_IS_NULL(immediate_parents))
1207 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_METHOD_NOT_FOUND,
1208 "Failed to get parents list from class!");
1210 parent_count = VTABLE_elements(interp, immediate_parents);
1212 if (parent_count == 0) {
1213 /* No parents - MRO just contains this class. */
1214 result = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1215 VTABLE_push_pmc(interp, result, _class);
1216 return result;
1219 /* Otherwise, need to do merge. For that, need linearizations of all of
1220 * our parents added to the merge list. */
1221 merge_list = PMCNULL;
1222 for (i = 0; i < parent_count; ++i) {
1223 PMC * const lin = Parrot_ComputeMRO_C3(interp,
1224 VTABLE_get_pmc_keyed_int(interp, immediate_parents, i));
1226 if (PMC_IS_NULL(lin))
1227 return PMCNULL;
1229 /* instantiated lazily */
1230 if (PMC_IS_NULL(merge_list))
1231 merge_list = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1233 VTABLE_push_pmc(interp, merge_list, lin);
1236 /* Finally, need list of direct parents on the end of the merge list, then
1237 * we can merge. */
1238 VTABLE_push_pmc(interp, merge_list, immediate_parents);
1239 result = C3_merge(interp, merge_list);
1241 if (PMC_IS_NULL(result))
1242 return PMCNULL;
1244 /* Merged result needs this class on the start, and then we're done. */
1245 VTABLE_unshift_pmc(interp, result, _class);
1247 return result;
1253 =item C<void Parrot_ComposeRole(PARROT_INTERP, PMC *role, PMC *exclude, int
1254 got_exclude, PMC *alias, int got_alias, PMC *methods_hash, PMC *roles_list)>
1256 Used by the Class and Object PMCs internally to compose a role into either of
1257 them. The C<role> parameter is the role that we are composing into the class
1258 or role. C<methods_hash> is the hash of method names to invokable PMCs that
1259 contains the methods the class or role has. C<roles_list> is the list of roles
1260 the the class or method does.
1262 The C<role> parameter is only dealt with by its external interface. Whether
1263 this routine is usable by any other object system implemented in Parrot very
1264 much depends on how closely the role composition semantics they want are to
1265 the default implementation.
1267 =cut
1271 PARROT_EXPORT
1272 void
1273 Parrot_ComposeRole(PARROT_INTERP, ARGIN(PMC *role),
1274 ARGIN(PMC *exclude), int got_exclude,
1275 ARGIN(PMC *alias), int got_alias,
1276 ARGIN(PMC *methods_hash), ARGIN(PMC *roles_list))
1278 ASSERT_ARGS(Parrot_ComposeRole)
1279 PMC *methods;
1280 PMC *methods_iter;
1281 PMC *roles_of_role;
1282 PMC *proposed_add_methods;
1284 INTVAL roles_of_role_count;
1285 INTVAL i;
1287 /* Check we have not already composed the role; if so, just ignore it. */
1288 INTVAL roles_count = VTABLE_elements(interp, roles_list);
1290 for (i = 0; i < roles_count; ++i)
1291 if (VTABLE_get_pmc_keyed_int(interp, roles_list, i) == role)
1292 return;
1294 /* Get the methods from the role. */
1295 Parrot_pcc_invoke_method_from_c_args(interp, role, CONST_STRING(interp, "methods"), "->P", &methods);
1297 if (PMC_IS_NULL(methods))
1298 return;
1300 /* We need to check for conflicts before we do the composition. We
1301 * put each method that would be OK to add into a proposal list, and
1302 * bail out right away if we find a problem. */
1303 proposed_add_methods = Parrot_pmc_new(interp, enum_class_Hash);
1304 methods_iter = VTABLE_get_iter(interp, methods);
1306 while (VTABLE_get_bool(interp, methods_iter)) {
1307 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1308 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp, methods,
1309 method_name);
1311 /* Need to find the name we'll check for a conflict on. */
1312 int excluded = 0;
1314 /* Check if it's in the exclude list. */
1315 if (got_exclude) {
1316 const int exclude_count = VTABLE_elements(interp, exclude);
1318 for (i = 0; i < exclude_count; ++i) {
1319 const STRING * const check =
1320 VTABLE_get_string_keyed_int(interp, exclude, i);
1322 if (Parrot_str_equal(interp, check, method_name)) {
1323 excluded = 1;
1324 break;
1329 /* If we weren't excluded... */
1330 if (!excluded) {
1331 /* Is there a method with this name already in the class? */
1333 if (VTABLE_exists_keyed_str(interp, methods_hash, method_name)) {
1334 /* Conflicts with something already in the class, unless it's a
1335 * multi-method. */
1336 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, method_name);
1337 if (PMC_IS_NULL(cur_entry) || !VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1338 Parrot_ex_throw_from_c_args(interp, NULL,
1339 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1340 "A conflict occurred during role composition "
1341 "due to method '%S'.", method_name);
1344 /* What about a conflict with ourslef? */
1345 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1346 method_name))
1347 /* Something very weird is going on. */
1348 Parrot_ex_throw_from_c_args(interp, NULL,
1349 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1350 "A conflict occurred during role composition;"
1351 " the method '%S' from the role managed to conflict "
1352 "with itself somehow.", method_name);
1354 /* If we got here, no conflicts! Add method to the "to compose"
1355 * list. */
1356 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1357 method_name, cur_method);
1360 /* Now see if we've got an alias. */
1361 if (got_alias && VTABLE_exists_keyed_str(interp, alias, method_name)) {
1362 /* Got one. Get name to alias it to. */
1363 STRING * const alias_name = VTABLE_get_string_keyed_str(interp,
1364 alias, method_name);
1366 /* Is there a method with this name already in the class? If it's
1367 * not a multi-method, error. */
1368 if (VTABLE_exists_keyed_str(interp, methods_hash, alias_name)) {
1369 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, alias_name);
1370 if (PMC_IS_NULL(cur_entry) || !VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1371 /* Conflicts with something already in the class. */
1372 Parrot_ex_throw_from_c_args(interp, NULL,
1373 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1374 "A conflict occurred during role composition"
1375 " due to the aliasing of '%S' to '%S'.",
1376 method_name, alias_name);
1379 /* What about a conflict with ourslef? */
1380 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1381 alias_name))
1382 Parrot_ex_throw_from_c_args(interp, NULL,
1383 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1384 "A conflict occurred during role composition"
1385 " due to the aliasing of '%S' to '%S' (role already has"
1386 " a method '%S').", method_name, alias_name, alias_name);
1388 /* If we get here, no conflicts! Add method to the "to compose"
1389 * list with its alias. */
1390 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1391 alias_name, cur_method);
1395 /* If we get here, we detected no conflicts. Go ahead and compose the
1396 * methods. */
1397 methods_iter = VTABLE_get_iter(interp, proposed_add_methods);
1399 while (VTABLE_get_bool(interp, methods_iter)) {
1400 /* Get current method and its name. */
1401 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1402 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp,
1403 proposed_add_methods, method_name);
1405 /* Add it to the methods of the class. */
1406 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, method_name);
1407 if (VTABLE_isa(interp, cur_method, CONST_STRING(interp, "MultiSub"))) {
1408 /* The thing we're adding is a multi-sub, but is the thing in the
1409 * class already a multi-sub? */
1410 if (!PMC_IS_NULL(cur_entry) && VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub"))) {
1411 /* Class already has a multi-sub; need to merge our methods into it. */
1412 const INTVAL num_subs = VTABLE_elements(interp, cur_method);
1413 INTVAL j;
1414 for (j = 0; j < num_subs; ++j)
1415 VTABLE_push_pmc(interp, cur_entry, VTABLE_get_pmc_keyed_int(interp,
1416 cur_method, j));
1418 else {
1419 /* It's not, and we didn't conflict so must be no entry. Just stick it in. */
1420 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1423 else {
1424 /* Are we adding into a multi-sub? */
1425 if (!PMC_IS_NULL(cur_entry) && VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1426 VTABLE_push_pmc(interp, cur_entry, cur_method);
1427 else
1428 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1432 /* Add this role to the roles list. */
1433 VTABLE_push_pmc(interp, roles_list, role);
1434 ++roles_count;
1436 /* As a result of composing this role, we will also now do the roles
1437 * that it did itself. Note that we already have the correct methods
1438 * as roles "flatten" the methods they get from other roles into their
1439 * own method list. */
1440 Parrot_pcc_invoke_method_from_c_args(interp, role, CONST_STRING(interp, "roles"), "->P", &roles_of_role);
1441 roles_of_role_count = VTABLE_elements(interp, roles_of_role);
1443 for (i = 0; i < roles_of_role_count; ++i) {
1444 /* Only add if we don't already have it in the list. */
1445 PMC * const cur_role = VTABLE_get_pmc_keyed_int(interp,
1446 roles_of_role, i);
1447 INTVAL j;
1449 for (j = 0; j < roles_count; ++j) {
1450 if (VTABLE_get_pmc_keyed_int(interp, roles_list, j) == cur_role) {
1451 /* We ain't be havin' it. */
1452 VTABLE_push_pmc(interp, roles_list, cur_role);
1461 =back
1463 =head1 SEE ALSO
1465 F<include/parrot/oo.h>, F<include/parrot/oo_private.h>,
1466 F<docs/pdds/pdd15_objects.pod>.
1468 =cut
1473 * Local variables:
1474 * c-file-style: "parrot"
1475 * End:
1476 * vim: expandtab shiftwidth=4: