2 Copyright (C) 2007-2009, Parrot Foundation.
7 oo.c - Class and object
11 Handles class and object manipulation.
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"
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
,
57 ARGIN(STRING
*method_name
))
58 __attribute__nonnull__(1)
59 __attribute__nonnull__(2)
60 __attribute__nonnull__(3);
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,
102 Extract methods and vtable overrides from the given namespace and insert them
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. */
119 /* Import any methods. */
120 Parrot_PCCINVOKE(interp
, ns
, CONST_STRING(interp
, "get_associated_methods"), "->P", &methods
);
122 if (!PMC_IS_NULL(methods
)) {
123 PMC
* const iter
= VTABLE_get_iter(interp
, methods
);
125 while (VTABLE_get_bool(interp
, iter
)) {
126 STRING
* const meth_name
= VTABLE_shift_string(interp
, iter
);
127 PMC
* const meth_sub
= VTABLE_get_pmc_keyed_str(interp
, methods
,
129 VTABLE_add_method(interp
, self
, meth_name
, meth_sub
);
133 /* Import any vtable methods. */
134 Parrot_PCCINVOKE(interp
, ns
, CONST_STRING(interp
, "get_associated_vtable_methods"), "->P", &vtable_overrides
);
136 if (!PMC_IS_NULL(vtable_overrides
)) {
137 PMC
* const iter
= VTABLE_get_iter(interp
, vtable_overrides
);
138 while (VTABLE_get_bool(interp
, iter
)) {
139 STRING
* const vtable_index_str
= VTABLE_shift_string(interp
, iter
);
140 PMC
* const vtable_sub
= VTABLE_get_pmc_keyed_str(interp
,
141 vtable_overrides
, vtable_index_str
);
143 /* Look up the name of the vtable function from the index. */
144 const INTVAL vtable_index
= Parrot_str_to_int(interp
, vtable_index_str
);
145 const char * const meth_c
= Parrot_vtable_slot_names
[vtable_index
];
146 STRING
* const vtable_name
= Parrot_str_new(interp
, meth_c
, 0);
147 VTABLE_add_vtable_override(interp
, self
, vtable_name
, vtable_sub
);
155 =item C<PMC * Parrot_oo_get_namespace(PARROT_INTERP, const PMC *classobj)>
157 Lookup a namespace object from a class PMC.
163 PARROT_CAN_RETURN_NULL
164 PARROT_WARN_UNUSED_RESULT
166 Parrot_oo_get_namespace(SHIM_INTERP
, ARGIN(const PMC
*classobj
))
168 ASSERT_ARGS(Parrot_oo_get_namespace
)
169 Parrot_Class_attributes
* const _class
= PARROT_CLASS(classobj
);
170 PMC
* const _namespace
= _class
->_namespace
;
172 if (PMC_IS_NULL(_namespace
))
181 =item C<PMC * Parrot_oo_get_class(PARROT_INTERP, PMC *key)>
183 Lookup a class object from a namespace, string, or key PMC.
190 PARROT_CAN_RETURN_NULL
191 PARROT_WARN_UNUSED_RESULT
193 Parrot_oo_get_class(PARROT_INTERP
, ARGIN(PMC
*key
))
195 ASSERT_ARGS(Parrot_oo_get_class
)
196 PMC
*classobj
= PMCNULL
;
198 if (PObj_is_class_TEST(key
))
201 /* Fast select of behavior based on type of the lookup key */
202 switch (key
->vtable
->base_type
) {
203 case enum_class_NameSpace
:
204 classobj
= VTABLE_get_class(interp
, key
);
206 case enum_class_String
:
208 case enum_class_ResizableStringArray
:
210 PMC
* const hll_ns
= VTABLE_get_pmc_keyed_int(interp
,
211 interp
->HLL_namespace
,
212 Parrot_pcc_get_HLL(interp
, CURRENT_CONTEXT(interp
)));
213 PMC
* const ns
= Parrot_get_namespace_keyed(interp
,
216 if (!PMC_IS_NULL(ns
))
217 classobj
= VTABLE_get_class(interp
, ns
);
224 /* If the PMCProxy doesn't exist yet for the given key, we look up the
225 type ID here and create a new one */
226 if (PMC_IS_NULL(classobj
)) {
228 const INTVAL base_type
= key
->vtable
->base_type
;
230 /* This is a hack! All PMCs should be able to be handled through
231 a single codepath, and all of them should be able to avoid
232 stringification because it's so imprecise. */
233 if (base_type
== enum_class_Key
234 || base_type
== enum_class_ResizableStringArray
235 || base_type
== enum_class_String
)
236 type
= pmc_type_p(interp
, key
);
238 type
= pmc_type(interp
, VTABLE_get_string(interp
, key
));
240 classobj
= get_pmc_proxy(interp
, type
);
248 =item C<PMC * Parrot_oo_clone_object(PARROT_INTERP, PMC *pmc, PMC *class_, PMC
251 Clone an Object PMC. If an existing PMC C<dest> is provided, reuse that
252 PMC to store copies of the data. Otherwise, create a new PMC and populate
259 PARROT_CANNOT_RETURN_NULL
261 Parrot_oo_clone_object(PARROT_INTERP
, ARGIN(PMC
*pmc
),
262 ARGMOD_NULLOK(PMC
*class_
), ARGMOD_NULLOK(PMC
*dest
))
264 ASSERT_ARGS(Parrot_oo_clone_object
)
265 Parrot_Object_attributes
*obj
;
266 Parrot_Object_attributes
*cloned_guts
;
267 Parrot_Class_attributes
*_class
;
272 if (!PMC_IS_NULL(dest
)) {
273 PARROT_ASSERT(!PMC_IS_NULL(class_
));
274 PARROT_ASSERT(class_
->vtable
->base_type
== enum_class_Class
);
275 obj
= PARROT_OBJECT(pmc
);
279 obj
= PARROT_OBJECT(pmc
);
280 cloned
= pmc_new_noinit(interp
, enum_class_Object
);
283 _class
= PARROT_CLASS(obj
->_class
);
284 PARROT_ASSERT(_class
);
285 num_classes
= VTABLE_elements(interp
, _class
->all_parents
);
287 /* Set custom GC mark and destroy on the object. */
288 PObj_custom_mark_SET(cloned
);
289 PObj_custom_destroy_SET(cloned
);
291 /* Flag that it is an object */
292 PObj_is_object_SET(cloned
);
294 /* Now clone attributes list.class. */
295 cloned_guts
= (Parrot_Object_attributes
*) PMC_data(cloned
);
296 cloned_guts
->_class
= obj
->_class
;
297 cloned_guts
->attrib_store
= NULL
;
298 cloned_guts
->attrib_store
= VTABLE_clone(interp
, obj
->attrib_store
);
299 num_attrs
= VTABLE_elements(interp
, cloned_guts
->attrib_store
);
300 for (i
= 0; i
< num_attrs
; i
++) {
301 PMC
* const to_clone
= VTABLE_get_pmc_keyed_int(interp
, cloned_guts
->attrib_store
, i
);
302 if (!PMC_IS_NULL(to_clone
)) {
303 VTABLE_set_pmc_keyed_int(interp
, cloned_guts
->attrib_store
, i
,
304 VTABLE_clone(interp
, to_clone
));
308 /* Some of the attributes may have been the PMCs providing storage for any
309 * PMCs we inherited from; also need to clone those. */
310 if (CLASS_has_alien_parents_TEST(obj
->_class
)) {
311 /* Locate any PMC parents. */
312 for (i
= 0; i
< num_classes
; i
++) {
313 PMC
* const cur_class
= VTABLE_get_pmc_keyed_int(interp
, _class
->all_parents
, i
);
314 if (cur_class
->vtable
->base_type
== enum_class_PMCProxy
) {
315 /* Clone this PMC too. */
316 STRING
* const proxy
= CONST_STRING(interp
, "proxy");
317 VTABLE_set_attr_keyed(interp
, cloned
, cur_class
, proxy
,
319 VTABLE_get_attr_keyed(interp
, cloned
, cur_class
, proxy
)));
324 /* And we have ourselves a clone. */
330 =item C<void * Parrot_oo_new_object_attrs(PARROT_INTERP, PMC * class_)>
332 Create a new C<Parrot_Object_attributes> structure to hold data for an Object
333 PMC. We need this for places which create a new Object without instantiating it
334 through its associated class, such as in C<Parrot_oo_clone_object>.
340 PARROT_CANNOT_RETURN_NULL
342 Parrot_oo_new_object_attrs(PARROT_INTERP
, ARGIN(PMC
* class_
))
344 ASSERT_ARGS(Parrot_oo_new_object_attrs
)
345 Parrot_Object_attributes
* const obj_guts
=
346 mem_allocate_typed(Parrot_Object_attributes
);
347 obj_guts
->_class
= class_
;
348 obj_guts
->attrib_store
= pmc_new(interp
, enum_class_ResizablePMCArray
);
349 return (void *)obj_guts
;
354 =item C<static PMC * get_pmc_proxy(PARROT_INTERP, INTVAL type)>
356 Get the PMC proxy for a PMC with the given type, creating it if does not exist.
357 If type is not a valid type, return PMCNULL. This code assumes that
358 all PMCProxy objects live in the 'parrot' HLL namespace -- if/when
359 we allow PMC types to exist in other HLL namespaces, this code will
362 For internal use only.
369 PARROT_CANNOT_RETURN_NULL
370 PARROT_WARN_UNUSED_RESULT
372 get_pmc_proxy(PARROT_INTERP
, INTVAL type
)
374 ASSERT_ARGS(get_pmc_proxy
)
377 /* Check if not a PMC or invalid type number */
378 if (type
> interp
->n_vtable_max
|| type
<= 0)
381 type_class
= interp
->vtables
[type
]->pmc_class
;
382 if (type
!= enum_class_Class
383 && type_class
->vtable
->base_type
== enum_class_Class
) {
387 PMC
* const parrot_hll
= Parrot_get_namespace_keyed_str(interp
, interp
->root_namespace
, CONST_STRING(interp
, "parrot"));
389 Parrot_make_namespace_keyed_str(interp
, parrot_hll
,
390 interp
->vtables
[type
]->whoami
);
391 PMC
* proxy
= VTABLE_get_class(interp
, pmc_ns
);
393 /* Create proxy if not found */
394 if (PMC_IS_NULL(proxy
)) {
395 PMC
* const type_num
= pmc_new(interp
, enum_class_Integer
);
396 VTABLE_set_integer_native(interp
, type_num
, type
);
397 proxy
= pmc_new_init(interp
, enum_class_PMCProxy
, type_num
);
398 Parrot_PCCINVOKE(interp
, pmc_ns
, CONST_STRING(interp
, "set_class"), "P->", proxy
);
406 =item C<PMC * Parrot_oo_get_class_str(PARROT_INTERP, STRING *name)>
408 Lookup a class object from a builtin string.
415 PARROT_CAN_RETURN_NULL
416 PARROT_WARN_UNUSED_RESULT
418 Parrot_oo_get_class_str(PARROT_INTERP
, ARGIN(STRING
*name
))
420 ASSERT_ARGS(Parrot_oo_get_class_str
)
422 /* First check in current HLL namespace */
423 PMC
* const hll_ns
= VTABLE_get_pmc_keyed_int(interp
, interp
->HLL_namespace
,
424 Parrot_pcc_get_HLL(interp
, CURRENT_CONTEXT(interp
)));
425 PMC
* const ns
= Parrot_get_namespace_keyed_str(interp
, hll_ns
, name
);
426 PMC
* const _class
= PMC_IS_NULL(ns
)
427 ? PMCNULL
: VTABLE_get_class(interp
, ns
);
429 /* If not found, check for a PMC */
430 if (PMC_IS_NULL(_class
))
431 return get_pmc_proxy(interp
, pmc_type(interp
, name
));
439 =item C<PMC * Parrot_oo_newclass_from_str(PARROT_INTERP, STRING *name)>
441 Create a new class object from a string name.
447 PARROT_CAN_RETURN_NULL
448 PARROT_WARN_UNUSED_RESULT
450 Parrot_oo_newclass_from_str(PARROT_INTERP
, ARGIN(STRING
*name
))
452 ASSERT_ARGS(Parrot_oo_newclass_from_str
)
453 PMC
* const namearg
= pmc_new(interp
, enum_class_String
);
454 PMC
* const namehash
= pmc_new(interp
, enum_class_Hash
);
457 VTABLE_set_string_native(interp
, namearg
, name
);
458 VTABLE_set_pmc_keyed_str(interp
, namehash
, CONST_STRING(interp
, "name"), namearg
);
460 classobj
= pmc_new_init(interp
, enum_class_Class
, namehash
);
462 PARROT_ASSERT(classobj
);
469 =item C<PMC * Parrot_oo_find_vtable_override_for_class(PARROT_INTERP, PMC
470 *classobj, STRING *name)>
472 Lookup a vtable override in a specific class object.
479 PARROT_CAN_RETURN_NULL
480 PARROT_WARN_UNUSED_RESULT
482 Parrot_oo_find_vtable_override_for_class(PARROT_INTERP
,
483 ARGIN(PMC
*classobj
), ARGIN(STRING
*name
))
485 ASSERT_ARGS(Parrot_oo_find_vtable_override_for_class
)
486 Parrot_Class_attributes
*class_info
;
487 PARROT_ASSERT(PObj_is_class_TEST(classobj
));
489 class_info
= PARROT_CLASS(classobj
);
490 return VTABLE_get_pmc_keyed_str(interp
, class_info
->vtable_overrides
, name
);
496 =item C<PMC * Parrot_oo_find_vtable_override(PARROT_INTERP, PMC *classobj,
499 Lookup a vtable override in a class, including any vtable overrides inherited
507 PARROT_CAN_RETURN_NULL
508 PARROT_WARN_UNUSED_RESULT
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
);
516 VTABLE_get_pmc_keyed_str(interp
, _class
->parent_overrides
, name
);
518 if (PMC_IS_NULL(result
)) {
519 /* Walk and search for the vtable method. */
520 const INTVAL num_classes
= VTABLE_elements(interp
, _class
->all_parents
);
523 for (i
= 0; i
< num_classes
; i
++) {
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
,
531 if (!PMC_IS_NULL(result
))
534 if (PMC_IS_NULL(result
))
535 result
= pmc_new(interp
, enum_class_Undef
);
536 VTABLE_set_pmc_keyed_str(interp
, _class
->parent_overrides
, name
, result
);
538 if (result
->vtable
->base_type
== enum_class_Undef
)
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.
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
;
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
);
572 Parrot_str_free_cstring(name_c
);
581 Parrot_str_free_cstring(name_c
);
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.
600 PARROT_CAN_RETURN_NULL
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
) {
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.
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
))
641 switch (VTABLE_type(interp
, value
)) {
642 case enum_class_NameSpace
:
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
);
657 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INTERP_ERROR
,
658 "Unrecognized class name PMC type");
666 =item C<INTVAL Parrot_oo_register_type(PARROT_INTERP, PMC *name, PMC
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.
677 PARROT_WARN_UNUSED_RESULT
679 Parrot_oo_register_type(PARROT_INTERP
, ARGIN(PMC
*name
), ARGIN(PMC
*_namespace
))
681 ASSERT_ARGS(Parrot_oo_register_type
)
683 const INTVAL typeid_exists
= fail_if_type_exists(interp
, name
);
685 PMC
* const classobj
= VTABLE_get_class(interp
, _namespace
);
686 if (!PMC_IS_NULL(classobj
)) {
687 STRING
* const classname
= VTABLE_get_string(interp
, _namespace
);
688 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
689 "Class %Ss already registered!\n",
690 Parrot_str_escape(interp
, classname
));
693 /* Type doesn't exist, so go ahead and register it. Lock interpreter so
694 * pt_shared_fixup() can safely do a type lookup. */
695 LOCK_INTERPRETER(interp
);
697 type
= get_new_vtable_index(interp
);
700 if (!typeid_exists
) {
701 PMC
* const classname_hash
= interp
->class_hash
;
702 PMC
* const item
= pmc_new(interp
, enum_class_Integer
);
703 /* set entry in name->type hash */
704 VTABLE_set_integer_native(interp
, item
, type
);
706 VTABLE_set_pmc_keyed(interp
, classname_hash
, name
, item
);
709 UNLOCK_INTERPRETER(interp
);
716 =item C<void mark_object_cache(PARROT_INTERP)>
718 Marks all PMCs in the object method cache as live. This shouldn't strictly be
719 necessary, as they're likely all reachable from namespaces and classes, but
720 it's unlikely to hurt anything except mark phase performance.
726 #define TBL_SIZE_MASK 0x1ff /* x bits 2..10 */
727 #define TBL_SIZE (1 + TBL_SIZE_MASK)
730 mark_object_cache(PARROT_INTERP
)
732 ASSERT_ARGS(mark_object_cache
)
733 Caches
* const mc
= interp
->caches
;
739 for (type
= 0; type
< mc
->mc_size
; type
++) {
743 for (entry
= 0; entry
< TBL_SIZE
; ++entry
) {
744 Meth_cache_entry
*e
= mc
->idx
[type
][entry
];
746 Parrot_gc_mark_PObj_alive(interp
, (PObj
*)e
->pmc
);
756 =item C<void init_object_cache(PARROT_INTERP)>
758 Allocate memory for object cache.
765 init_object_cache(PARROT_INTERP
)
767 ASSERT_ARGS(init_object_cache
)
768 Caches
* const mc
= interp
->caches
= mem_allocate_zeroed_typed(Caches
);
775 =item C<void destroy_object_cache(PARROT_INTERP)>
777 Destroy the object cache. Loop over all caches and invalidate them. Then
778 free the caches back to the OS.
785 destroy_object_cache(PARROT_INTERP
)
787 ASSERT_ARGS(destroy_object_cache
)
789 Caches
* const mc
= interp
->caches
;
791 /* mc->idx[type][bits] = e; */
792 for (i
= 0; i
< mc
->mc_size
; i
++) {
794 invalidate_type_caches(interp
, i
);
797 mem_sys_free(mc
->idx
);
804 =item C<static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)>
806 Invalidate the cache of the specified type. Free each entry and then free
814 invalidate_type_caches(PARROT_INTERP
, UINTVAL type
)
816 ASSERT_ARGS(invalidate_type_caches
)
817 Caches
* const mc
= interp
->caches
;
823 /* is it a valid entry */
824 if (type
>= mc
->mc_size
|| !mc
->idx
[type
])
827 for (i
= 0; i
< TBL_SIZE
; ++i
) {
828 Meth_cache_entry
*e
= mc
->idx
[type
][i
];
830 Meth_cache_entry
* const next
= e
->next
;
836 mem_sys_free(mc
->idx
[type
]);
837 mc
->idx
[type
] = NULL
;
843 =item C<static void invalidate_all_caches(PARROT_INTERP)>
845 Invalidate all caches by looping over each cache and calling
846 C<invalidate_type_caches> on them.
853 invalidate_all_caches(PARROT_INTERP
)
855 ASSERT_ARGS(invalidate_all_caches
)
857 for (i
= 1; i
< (UINTVAL
)interp
->n_vtable_max
; ++i
)
858 invalidate_type_caches(interp
, i
);
864 =item C<void Parrot_invalidate_method_cache(PARROT_INTERP, STRING *_class)>
866 Clear method cache for the given class. If class is NULL, caches for
867 all classes are invalidated.
875 Parrot_invalidate_method_cache(PARROT_INTERP
, ARGIN_NULLOK(STRING
*_class
))
877 ASSERT_ARGS(Parrot_invalidate_method_cache
)
880 /* during interp creation and NCI registration the class_hash
882 if (!interp
->class_hash
)
885 if (interp
->resume_flag
& RESUME_INITIAL
)
889 invalidate_all_caches(interp
);
893 type
= pmc_type(interp
, _class
);
896 invalidate_all_caches(interp
);
898 invalidate_type_caches(interp
, (UINTVAL
)type
);
903 * quick'n'dirty method cache
904 * RT #45987: use a hash if method_name is not constant
905 * i.e. from obj.$Sreg(args)
906 * If this hash is implemented mark it during GC
911 =item C<PMC * Parrot_find_method_direct(PARROT_INTERP, PMC *_class, STRING
914 Find a method PMC for a named method, given the class PMC, current
915 interpreter, and name of the method. Don't use a possible method cache.
922 PARROT_CAN_RETURN_NULL
923 PARROT_WARN_UNUSED_RESULT
925 Parrot_find_method_direct(PARROT_INTERP
, ARGIN(PMC
*_class
), ARGIN(STRING
*method_name
))
927 ASSERT_ARGS(Parrot_find_method_direct
)
928 PMC
* const found
= find_method_direct_1(interp
, _class
, method_name
);
930 if (!PMC_IS_NULL(found
))
934 if (Parrot_str_equal(interp
, method_name
, CONST_STRING(interp
, "__get_string")))
935 return find_method_direct_1(interp
, _class
, CONST_STRING(interp
, "__get_repr"));
943 =item C<PMC * Parrot_find_method_with_cache(PARROT_INTERP, PMC *_class, STRING
946 Find a method PMC for a named method, given the class PMC, current
947 interp, and name of the method.
949 This routine should use the current scope's method cache, if there is
950 one. If not, it creates a new method cache. Or, rather, it will when
951 we've got that bit working. For now it unconditionally goes and looks up
952 the name in the global stash.
959 PARROT_CAN_RETURN_NULL
960 PARROT_WARN_UNUSED_RESULT
962 Parrot_find_method_with_cache(PARROT_INTERP
, ARGIN(PMC
*_class
), ARGIN(STRING
*method_name
))
964 ASSERT_ARGS(Parrot_find_method_with_cache
)
968 Meth_cache_entry
*e
, *old
;
970 PARROT_ASSERT(method_name
!= 0);
972 #if DISABLE_METH_CACHE
973 return Parrot_find_method_direct(interp
, _class
, method_name
);
976 if (! PObj_constant_TEST(method_name
))
977 return Parrot_find_method_direct(interp
, _class
, method_name
);
980 type
= _class
->vtable
->base_type
;
981 bits
= (((UINTVAL
) method_name
->strstart
) >> 2) & TBL_SIZE_MASK
;
983 if (type
>= mc
->mc_size
) {
985 mc
->idx
= (Meth_cache_entry
***)mem_sys_realloc_zeroed(mc
->idx
,
986 sizeof (Meth_cache_entry
***) * (type
+ 1),
987 sizeof (Meth_cache_entry
***) * mc
->mc_size
);
990 mc
->idx
= mem_allocate_n_zeroed_typed(type
+ 1, Meth_cache_entry
**);
992 mc
->mc_size
= type
+ 1;
995 if (mc
->idx
[type
] == NULL
) {
996 mc
->idx
[type
] = (Meth_cache_entry
**)mem_sys_allocate_zeroed(
997 sizeof (Meth_cache_entry
*) * TBL_SIZE
);
1000 e
= mc
->idx
[type
][bits
];
1003 while (e
&& e
->strstart
!= method_name
->strstart
) {
1009 /* when here no or no correct entry was at [bits] */
1010 e
= mem_allocate_typed(Meth_cache_entry
);
1015 mc
->idx
[type
][bits
] = e
;
1017 e
->pmc
= Parrot_find_method_direct(interp
, _class
, method_name
);
1019 e
->strstart
= method_name
->strstart
;
1028 =item C<static void debug_trace_find_meth(PARROT_INTERP, const PMC *_class,
1029 const STRING *name, const PMC *sub)>
1031 Print some information about the search for a sub.
1038 # define TRACE_FM(i, c, m, sub)
1040 # define TRACE_FM(i, c, m, sub) \
1041 debug_trace_find_meth((i), (c), (m), (sub))
1044 debug_trace_find_meth(PARROT_INTERP
, ARGIN(const PMC
*_class
),
1045 ARGIN(const STRING
*name
), ARGIN_NULLOK(const PMC
*sub
))
1047 ASSERT_ARGS(debug_trace_find_meth
)
1052 if (!Interp_trace_TEST(interp
, PARROT_TRACE_FIND_METH_FLAG
))
1055 if (PObj_is_class_TEST(_class
)) {
1056 SLOTTYPE
* const class_array
= PMC_data_typed(_class
, SLOTTYPE
*);
1057 PMC
* const class_name_pmc
= get_attrib_num(class_array
, PCD_CLASS_NAME
);
1058 class_name
= VTABLE_get_string(interp
, class_name_pmc
);
1061 class_name
= _class
->vtable
->whoami
;
1064 if (sub
->vtable
->base_type
== enum_class_NCI
)
1072 tracer
= (interp
->pdb
&& interp
->pdb
->debugger
) ?
1073 interp
->pdb
->debugger
:
1075 Parrot_io_eprintf(tracer
, "# find_method class '%Ss' method '%Ss': %s\n",
1076 class_name
, name
, result
);
1084 =item C<static PMC * find_method_direct_1(PARROT_INTERP, PMC *_class, STRING
1087 Find the method with the given name in the specified class.
1093 PARROT_WARN_UNUSED_RESULT
1094 PARROT_CAN_RETURN_NULL
1096 find_method_direct_1(PARROT_INTERP
, ARGIN(PMC
*_class
),
1097 ARGIN(STRING
*method_name
))
1099 ASSERT_ARGS(find_method_direct_1
)
1102 PMC
* const mro
= _class
->vtable
->mro
;
1103 const INTVAL n
= VTABLE_elements(interp
, mro
);
1105 for (i
= 0; i
< n
; ++i
) {
1108 _class
= VTABLE_get_pmc_keyed_int(interp
, mro
, i
);
1109 ns
= VTABLE_get_namespace(interp
, _class
);
1110 method
= VTABLE_get_pmc_keyed_str(interp
, ns
, method_name
);
1112 TRACE_FM(interp
, _class
, method_name
, method
);
1114 if (!PMC_IS_NULL(method
))
1118 TRACE_FM(interp
, _class
, method_name
, NULL
);
1125 =item C<static PMC* C3_merge(PARROT_INTERP, PMC *merge_list)>
1127 Merge together the MRO of the items in the list.
1133 PARROT_WARN_UNUSED_RESULT
1134 PARROT_CAN_RETURN_NULL
1136 C3_merge(PARROT_INTERP
, ARGIN(PMC
*merge_list
))
1138 ASSERT_ARGS(C3_merge
)
1139 PMC
*accepted
= PMCNULL
;
1140 PMC
*result
= PMCNULL
;
1141 const int list_count
= VTABLE_elements(interp
, merge_list
);
1145 /* Try and find something appropriate to add to the MRO - basically, the
1146 * first list head that is not in the tail of all the other lists. */
1147 for (i
= 0; i
< list_count
; i
++) {
1148 PMC
* const cand_list
= VTABLE_get_pmc_keyed_int(interp
, merge_list
, i
);
1154 if (VTABLE_elements(interp
, cand_list
) == 0)
1157 cand_class
= VTABLE_get_pmc_keyed_int(interp
, cand_list
, 0);
1160 for (j
= 0; j
< list_count
; j
++) {
1161 /* Skip the current list. */
1163 /* Is it in the tail? If so, reject. */
1164 PMC
* const check_list
=
1165 VTABLE_get_pmc_keyed_int(interp
, merge_list
, j
);
1167 const int check_length
= VTABLE_elements(interp
, check_list
);
1170 for (k
= 1; k
< check_length
; k
++) {
1171 if (VTABLE_get_pmc_keyed_int(interp
, check_list
, k
) ==
1180 /* If we didn't reject it, this candidate will do. */
1182 accepted
= cand_class
;
1187 /* If we never found any candidates, return an empty list. */
1188 if (cand_count
== 0)
1189 return pmc_new(interp
, enum_class_ResizablePMCArray
);
1191 /* If we didn't find anything to accept, error. */
1192 if (PMC_IS_NULL(accepted
))
1193 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_ILL_INHERIT
,
1194 "Could not build C3 linearization: ambiguous hierarchy");
1196 /* Otherwise, remove what was accepted from the merge lists. */
1197 for (i
= 0; i
< list_count
; i
++) {
1199 PMC
* const list
= VTABLE_get_pmc_keyed_int(interp
, merge_list
, i
);
1200 const INTVAL list_count
= VTABLE_elements(interp
, list
);
1203 for (j
= 0; j
< list_count
; j
++) {
1204 if (VTABLE_get_pmc_keyed_int(interp
, list
, j
) == accepted
) {
1205 VTABLE_delete_keyed_int(interp
, list
, j
);
1211 /* Need to merge what remains of the list, then put what was accepted on
1212 * the start of the list, and we're done. */
1213 result
= C3_merge(interp
, merge_list
);
1214 VTABLE_unshift_pmc(interp
, result
, accepted
);
1222 =item C<PMC* Parrot_ComputeMRO_C3(PARROT_INTERP, PMC *_class)>
1224 Computes the C3 linearization for the given class. C3 is an algorithm to
1225 compute the method resolution order (MRO) of a class that is inheriting
1226 from multiple parent classes (multiple inheritance). C3 was first described
1227 by Barrett et al at:
1229 F<http://192.220.96.201/dylan/linearization-oopsla96.html>
1236 PARROT_WARN_UNUSED_RESULT
1237 PARROT_CAN_RETURN_NULL
1239 Parrot_ComputeMRO_C3(PARROT_INTERP
, ARGIN(PMC
*_class
))
1241 ASSERT_ARGS(Parrot_ComputeMRO_C3
)
1243 PMC
* const immediate_parents
= VTABLE_inspect_str(interp
, _class
, CONST_STRING(interp
, "parents"));
1248 INTVAL parent_count
;
1250 /* Now get immediate parents list. */
1251 if (!immediate_parents
)
1252 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_METHOD_NOT_FOUND
,
1253 "Failed to get parents list from class!");
1255 parent_count
= VTABLE_elements(interp
, immediate_parents
);
1257 if (parent_count
== 0) {
1258 /* No parents - MRO just contains this class. */
1259 result
= pmc_new(interp
, enum_class_ResizablePMCArray
);
1260 VTABLE_push_pmc(interp
, result
, _class
);
1264 /* Otherwise, need to do merge. For that, need linearizations of all of
1265 * our parents added to the merge list. */
1266 merge_list
= PMCNULL
;
1267 for (i
= 0; i
< parent_count
; i
++) {
1268 PMC
* const lin
= Parrot_ComputeMRO_C3(interp
,
1269 VTABLE_get_pmc_keyed_int(interp
, immediate_parents
, i
));
1271 if (PMC_IS_NULL(lin
))
1274 /* instantiated lazily */
1275 if (PMC_IS_NULL(merge_list
))
1276 merge_list
= pmc_new(interp
, enum_class_ResizablePMCArray
);
1278 VTABLE_push_pmc(interp
, merge_list
, lin
);
1281 /* Finally, need list of direct parents on the end of the merge list, then
1283 VTABLE_push_pmc(interp
, merge_list
, immediate_parents
);
1284 result
= C3_merge(interp
, merge_list
);
1286 if (PMC_IS_NULL(result
))
1289 /* Merged result needs this class on the start, and then we're done. */
1290 VTABLE_unshift_pmc(interp
, result
, _class
);
1298 =item C<void Parrot_ComposeRole(PARROT_INTERP, PMC *role, PMC *exclude, int
1299 got_exclude, PMC *alias, int got_alias, PMC *methods_hash, PMC *roles_list)>
1301 Used by the Class and Object PMCs internally to compose a role into either of
1302 them. The C<role> parameter is the role that we are composing into the class
1303 or role. C<methods_hash> is the hash of method names to invokable PMCs that
1304 contains the methods the class or role has. C<roles_list> is the list of roles
1305 the the class or method does.
1307 The C<role> parameter is only dealt with by its external interface. Whether
1308 this routine is usable by any other object system implemented in Parrot very
1309 much depends on how closely the role composition semantics they want are to
1310 the default implementation.
1318 Parrot_ComposeRole(PARROT_INTERP
, ARGIN(PMC
*role
),
1319 ARGIN(PMC
*exclude
), int got_exclude
,
1320 ARGIN(PMC
*alias
), int got_alias
,
1321 ARGIN(PMC
*methods_hash
), ARGIN(PMC
*roles_list
))
1323 ASSERT_ARGS(Parrot_ComposeRole
)
1327 PMC
*proposed_add_methods
;
1329 INTVAL roles_of_role_count
;
1332 /* Check we have not already composed the role; if so, just ignore it. */
1333 INTVAL roles_count
= VTABLE_elements(interp
, roles_list
);
1335 for (i
= 0; i
< roles_count
; i
++)
1336 if (VTABLE_get_pmc_keyed_int(interp
, roles_list
, i
) == role
)
1339 /* Get the methods from the role. */
1340 Parrot_PCCINVOKE(interp
, role
, CONST_STRING(interp
, "methods"), "->P", &methods
);
1342 if (PMC_IS_NULL(methods
))
1345 /* We need to check for conflicts before we do the composition. We
1346 * put each method that would be OK to add into a proposal list, and
1347 * bail out right away if we find a problem. */
1348 proposed_add_methods
= pmc_new(interp
, enum_class_Hash
);
1349 methods_iter
= VTABLE_get_iter(interp
, methods
);
1351 while (VTABLE_get_bool(interp
, methods_iter
)) {
1352 STRING
* const method_name
= VTABLE_shift_string(interp
, methods_iter
);
1353 PMC
* const cur_method
= VTABLE_get_pmc_keyed_str(interp
, methods
,
1356 /* Need to find the name we'll check for a conflict on. */
1359 /* Check if it's in the exclude list. */
1361 const int exclude_count
= VTABLE_elements(interp
, exclude
);
1363 for (i
= 0; i
< exclude_count
; i
++) {
1364 const STRING
* const check
=
1365 VTABLE_get_string_keyed_int(interp
, exclude
, i
);
1367 if (Parrot_str_equal(interp
, check
, method_name
)) {
1374 /* If we weren't excluded... */
1376 /* Is there a method with this name already in the class? */
1378 if (VTABLE_exists_keyed_str(interp
, methods_hash
, method_name
)) {
1379 /* Conflicts with something already in the class, unless it's a
1381 PMC
* const cur_entry
= VTABLE_get_pmc_keyed_str(interp
, methods_hash
, method_name
);
1382 if (PMC_IS_NULL(cur_entry
) || !VTABLE_isa(interp
, cur_entry
, CONST_STRING(interp
, "MultiSub")))
1383 Parrot_ex_throw_from_c_args(interp
, NULL
,
1384 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT
,
1385 "A conflict occurred during role composition "
1386 "due to method '%S'.", method_name
);
1389 /* What about a conflict with ourslef? */
1390 if (VTABLE_exists_keyed_str(interp
, proposed_add_methods
,
1392 /* Something very weird is going on. */
1393 Parrot_ex_throw_from_c_args(interp
, NULL
,
1394 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT
,
1395 "A conflict occurred during role composition;"
1396 " the method '%S' from the role managed to conflict "
1397 "with itself somehow.", method_name
);
1399 /* If we got here, no conflicts! Add method to the "to compose"
1401 VTABLE_set_pmc_keyed_str(interp
, proposed_add_methods
,
1402 method_name
, cur_method
);
1405 /* Now see if we've got an alias. */
1406 if (got_alias
&& VTABLE_exists_keyed_str(interp
, alias
, method_name
)) {
1407 /* Got one. Get name to alias it to. */
1408 STRING
* const alias_name
= VTABLE_get_string_keyed_str(interp
,
1409 alias
, method_name
);
1411 /* Is there a method with this name already in the class? If it's
1412 * not a multi-method, error. */
1413 if (VTABLE_exists_keyed_str(interp
, methods_hash
, alias_name
)) {
1414 PMC
* const cur_entry
= VTABLE_get_pmc_keyed_str(interp
, methods_hash
, alias_name
);
1415 if (PMC_IS_NULL(cur_entry
) || !VTABLE_isa(interp
, cur_entry
, CONST_STRING(interp
, "MultiSub")))
1416 /* Conflicts with something already in the class. */
1417 Parrot_ex_throw_from_c_args(interp
, NULL
,
1418 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT
,
1419 "A conflict occurred during role composition"
1420 " due to the aliasing of '%S' to '%S'.",
1421 method_name
, alias_name
);
1424 /* What about a conflict with ourslef? */
1425 if (VTABLE_exists_keyed_str(interp
, proposed_add_methods
,
1427 Parrot_ex_throw_from_c_args(interp
, NULL
,
1428 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT
,
1429 "A conflict occurred during role composition"
1430 " due to the aliasing of '%S' to '%S' (role already has"
1431 " a method '%S').", method_name
, alias_name
, alias_name
);
1433 /* If we get here, no conflicts! Add method to the "to compose"
1434 * list with its alias. */
1435 VTABLE_set_pmc_keyed_str(interp
, proposed_add_methods
,
1436 alias_name
, cur_method
);
1440 /* If we get here, we detected no conflicts. Go ahead and compose the
1442 methods_iter
= VTABLE_get_iter(interp
, proposed_add_methods
);
1444 while (VTABLE_get_bool(interp
, methods_iter
)) {
1445 /* Get current method and its name. */
1446 STRING
* const method_name
= VTABLE_shift_string(interp
, methods_iter
);
1447 PMC
* const cur_method
= VTABLE_get_pmc_keyed_str(interp
,
1448 proposed_add_methods
, method_name
);
1450 /* Add it to the methods of the class. */
1451 PMC
* const cur_entry
= VTABLE_get_pmc_keyed_str(interp
, methods_hash
, method_name
);
1452 if (VTABLE_isa(interp
, cur_method
, CONST_STRING(interp
, "MultiSub"))) {
1453 /* The thing we're adding is a multi-sub, but is the thing in the
1454 * class already a multi-sub? */
1455 if (!PMC_IS_NULL(cur_entry
) && VTABLE_isa(interp
, cur_entry
, CONST_STRING(interp
, "MultiSub"))) {
1456 /* Class already has a multi-sub; need to merge our methods into it. */
1457 const INTVAL num_subs
= VTABLE_elements(interp
, cur_method
);
1459 for (j
= 0; j
< num_subs
; j
++)
1460 VTABLE_push_pmc(interp
, cur_entry
, VTABLE_get_pmc_keyed_int(interp
,
1464 /* It's not, and we didn't conflict so must be no entry. Just stick it in. */
1465 VTABLE_set_pmc_keyed_str(interp
, methods_hash
, method_name
, cur_method
);
1469 /* Are we adding into a multi-sub? */
1470 if (!PMC_IS_NULL(cur_entry
) && VTABLE_isa(interp
, cur_entry
, CONST_STRING(interp
, "MultiSub")))
1471 VTABLE_push_pmc(interp
, cur_entry
, cur_method
);
1473 VTABLE_set_pmc_keyed_str(interp
, methods_hash
, method_name
, cur_method
);
1477 /* Add this role to the roles list. */
1478 VTABLE_push_pmc(interp
, roles_list
, role
);
1481 /* As a result of composing this role, we will also now do the roles
1482 * that it did itself. Note that we already have the correct methods
1483 * as roles "flatten" the methods they get from other roles into their
1484 * own method list. */
1485 Parrot_PCCINVOKE(interp
, role
, CONST_STRING(interp
, "roles"), "->P", &roles_of_role
);
1486 roles_of_role_count
= VTABLE_elements(interp
, roles_of_role
);
1488 for (i
= 0; i
< roles_of_role_count
; i
++) {
1489 /* Only add if we don't already have it in the list. */
1490 PMC
* const cur_role
= VTABLE_get_pmc_keyed_int(interp
,
1494 for (j
= 0; j
< roles_count
; j
++) {
1495 if (VTABLE_get_pmc_keyed_int(interp
, roles_list
, j
) == cur_role
) {
1496 /* We ain't be havin' it. */
1497 VTABLE_push_pmc(interp
, roles_list
, cur_role
);
1510 F<include/parrot/oo.h>, F<include/parrot/oo_private.h>,
1511 F<docs/pdds/pdd15_objects.pod>.
1519 * c-file-style: "parrot"
1521 * vim: expandtab shiftwidth=4: