revert - didn't do what I wanted
[parrot.git] / src / oo.c
blob30f565530be3a884bf04788d257ee5946e215679
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 =cut
163 PARROT_EXPORT
164 PARROT_CAN_RETURN_NULL
165 PARROT_WARN_UNUSED_RESULT
166 PMC *
167 Parrot_oo_get_class(PARROT_INTERP, ARGIN(PMC *key))
169 ASSERT_ARGS(Parrot_oo_get_class)
170 PMC *classobj = PMCNULL;
172 if (!PMC_IS_NULL(key)) {
173 if (PObj_is_class_TEST(key))
174 classobj = key;
175 else {
176 /* Fast select of behavior based on type of the lookup key */
177 switch (key->vtable->base_type) {
178 case enum_class_NameSpace:
179 classobj = VTABLE_get_class(interp, key);
180 break;
181 case enum_class_String:
182 case enum_class_Key:
183 case enum_class_ResizableStringArray:
185 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp,
186 interp->HLL_namespace,
187 Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
188 PMC * const ns = Parrot_get_namespace_keyed(interp,
189 hll_ns, key);
191 if (!PMC_IS_NULL(ns))
192 classobj = VTABLE_get_class(interp, ns);
194 default:
195 break;
199 /* If the PMCProxy doesn't exist yet for the given key, we look up the
200 type ID here and create a new one */
201 if (PMC_IS_NULL(classobj)) {
202 INTVAL type;
203 const INTVAL base_type = key->vtable->base_type;
205 /* This is a hack! All PMCs should be able to be handled through
206 a single codepath, and all of them should be able to avoid
207 stringification because it's so imprecise. */
208 if (base_type == enum_class_Key
209 || base_type == enum_class_ResizableStringArray
210 || base_type == enum_class_String)
211 type = Parrot_pmc_get_type(interp, key);
212 else
213 type = Parrot_pmc_get_type_str(interp, VTABLE_get_string(interp, key));
215 classobj = get_pmc_proxy(interp, type);
219 return classobj;
224 =item C<PMC * Parrot_oo_clone_object(PARROT_INTERP, PMC *pmc, PMC *dest)>
226 Clone an Object PMC. If an existing PMC C<dest> is provided, reuse that
227 PMC to store copies of the data. Otherwise, create a new PMC and populate
228 that with the data.
230 =cut
234 PARROT_CANNOT_RETURN_NULL
235 PMC *
236 Parrot_oo_clone_object(PARROT_INTERP, ARGIN(PMC *pmc), ARGMOD_NULLOK(PMC *dest))
238 ASSERT_ARGS(Parrot_oo_clone_object)
239 Parrot_Object_attributes *obj = PARROT_OBJECT(pmc);
240 Parrot_Object_attributes *cloned_guts;
241 Parrot_Class_attributes *_class;
242 PMC *cloned;
243 INTVAL num_classes;
244 INTVAL i, num_attrs;
246 if (!PMC_IS_NULL(dest)) {
247 cloned = dest;
249 else {
250 cloned = Parrot_pmc_new_noinit(interp, enum_class_Object);
253 _class = PARROT_CLASS(obj->_class);
254 PARROT_ASSERT(_class);
255 num_classes = VTABLE_elements(interp, _class->all_parents);
257 /* Set custom GC mark and destroy on the object. */
258 PObj_custom_mark_SET(cloned);
259 PObj_custom_destroy_SET(cloned);
261 /* Flag that it is an object */
262 PObj_is_object_SET(cloned);
264 /* Now clone attributes list.class. */
265 cloned_guts = (Parrot_Object_attributes *) PMC_data(cloned);
266 cloned_guts->_class = obj->_class;
267 cloned_guts->attrib_store = NULL; /* XXX Do we need to set ->attrib_store twice? */
268 cloned_guts->attrib_store = VTABLE_clone(interp, obj->attrib_store);
269 num_attrs = VTABLE_elements(interp, cloned_guts->attrib_store);
270 for (i = 0; i < num_attrs; ++i) {
271 PMC * const to_clone = VTABLE_get_pmc_keyed_int(interp, cloned_guts->attrib_store, i);
272 if (!PMC_IS_NULL(to_clone)) {
273 VTABLE_set_pmc_keyed_int(interp, cloned_guts->attrib_store, i,
274 VTABLE_clone(interp, to_clone));
278 /* Some of the attributes may have been the PMCs providing storage for any
279 * PMCs we inherited from; also need to clone those. */
280 if (CLASS_has_alien_parents_TEST(obj->_class)) {
281 int j;
282 /* Locate any PMC parents. */
283 for (j = 0; j < num_classes; ++j) {
284 PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp, _class->all_parents, j);
285 if (cur_class->vtable->base_type == enum_class_PMCProxy) {
286 /* Clone this PMC too. */
287 STRING * const proxy = CONST_STRING(interp, "proxy");
288 VTABLE_set_attr_keyed(interp, cloned, cur_class, proxy,
289 VTABLE_clone(interp,
290 VTABLE_get_attr_keyed(interp, cloned, cur_class, proxy)));
295 /* And we have ourselves a clone. */
296 return cloned;
301 =item C<static PMC * get_pmc_proxy(PARROT_INTERP, INTVAL type)>
303 Get the PMC proxy for a PMC with the given type, creating it if does not exist.
304 If type is not a valid type, return PMCNULL. This code assumes that
305 all PMCProxy objects live in the 'parrot' HLL namespace -- if/when
306 we allow PMC types to exist in other HLL namespaces, this code will
307 need to be updated.
309 For internal use only.
311 =cut
315 PARROT_INLINE
316 PARROT_CANNOT_RETURN_NULL
317 PARROT_WARN_UNUSED_RESULT
318 static PMC *
319 get_pmc_proxy(PARROT_INTERP, INTVAL type)
321 ASSERT_ARGS(get_pmc_proxy)
322 PMC * type_class;
324 /* Check if not a PMC or invalid type number */
325 if (type > interp->n_vtable_max || type <= 0)
326 return PMCNULL;
328 type_class = interp->vtables[type]->pmc_class;
329 if (type != enum_class_Class
330 && type_class->vtable->base_type == enum_class_Class) {
331 return type_class;
333 else {
334 PMC * const parrot_hll = Parrot_get_namespace_keyed_str(interp, interp->root_namespace, CONST_STRING(interp, "parrot"));
335 PMC * const pmc_ns =
336 Parrot_make_namespace_keyed_str(interp, parrot_hll,
337 interp->vtables[type]->whoami);
338 PMC * proxy = VTABLE_get_class(interp, pmc_ns);
340 /* Create proxy if not found */
341 if (PMC_IS_NULL(proxy)) {
342 proxy = Parrot_pmc_new_init_int(interp, enum_class_PMCProxy, type);
343 Parrot_pcc_invoke_method_from_c_args(interp, pmc_ns, CONST_STRING(interp, "set_class"), "P->", proxy);
345 return proxy;
351 =item C<PMC * Parrot_oo_get_class_str(PARROT_INTERP, STRING *name)>
353 Lookup a class object from a builtin string.
355 =cut
359 PARROT_EXPORT
360 PARROT_CAN_RETURN_NULL
361 PARROT_WARN_UNUSED_RESULT
362 PMC *
363 Parrot_oo_get_class_str(PARROT_INTERP, ARGIN_NULLOK(STRING *name))
365 ASSERT_ARGS(Parrot_oo_get_class_str)
367 if (STRING_IS_NULL(name))
368 return PMCNULL;
369 else {
371 /* First check in current HLL namespace */
372 PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp, interp->HLL_namespace,
373 Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
374 PMC * const ns = Parrot_get_namespace_keyed_str(interp, hll_ns, name);
375 PMC * const _class = PMC_IS_NULL(ns)
376 ? PMCNULL : VTABLE_get_class(interp, ns);
378 /* If not found, check for a PMC */
379 if (PMC_IS_NULL(_class))
380 return get_pmc_proxy(interp, Parrot_pmc_get_type_str(interp, name));
381 else
382 return _class;
389 =item C<PMC * Parrot_oo_newclass_from_str(PARROT_INTERP, STRING *name)>
391 Create a new class object from a string name.
393 =cut
397 PARROT_CAN_RETURN_NULL
398 PARROT_WARN_UNUSED_RESULT
399 PMC *
400 Parrot_oo_newclass_from_str(PARROT_INTERP, ARGIN(STRING *name))
402 ASSERT_ARGS(Parrot_oo_newclass_from_str)
403 PMC * const namearg = Parrot_pmc_new(interp, enum_class_String);
404 PMC * const namehash = Parrot_pmc_new(interp, enum_class_Hash);
405 PMC *classobj;
407 VTABLE_set_string_native(interp, namearg, name);
408 VTABLE_set_pmc_keyed_str(interp, namehash, CONST_STRING(interp, "name"), namearg);
410 classobj = Parrot_pmc_new_init(interp, enum_class_Class, namehash);
412 PARROT_ASSERT(classobj);
413 return classobj;
419 =item C<PMC * Parrot_oo_find_vtable_override_for_class(PARROT_INTERP, PMC
420 *classobj, STRING *name)>
422 Lookup a vtable override in a specific class object.
424 =cut
428 PARROT_EXPORT
429 PARROT_CAN_RETURN_NULL
430 PARROT_WARN_UNUSED_RESULT
431 PMC *
432 Parrot_oo_find_vtable_override_for_class(PARROT_INTERP,
433 ARGIN(PMC *classobj), ARGIN(STRING *name))
435 ASSERT_ARGS(Parrot_oo_find_vtable_override_for_class)
436 const Parrot_Class_attributes * const class_info = PARROT_CLASS(classobj);;
437 PARROT_ASSERT(PObj_is_class_TEST(classobj));
439 return VTABLE_get_pmc_keyed_str(interp, class_info->vtable_overrides, name);
445 =item C<PMC * Parrot_oo_find_vtable_override(PARROT_INTERP, PMC *classobj,
446 STRING *name)>
448 Lookup a vtable override in a class, including any vtable overrides inherited
449 from parents.
451 =cut
455 PARROT_EXPORT
456 PARROT_CAN_RETURN_NULL
457 PARROT_WARN_UNUSED_RESULT
458 PMC *
459 Parrot_oo_find_vtable_override(PARROT_INTERP,
460 ARGIN(PMC *classobj), ARGIN(STRING *name))
462 ASSERT_ARGS(Parrot_oo_find_vtable_override)
463 Parrot_Class_attributes * const _class = PARROT_CLASS(classobj);
464 PMC *result =
465 VTABLE_get_pmc_keyed_str(interp, _class->parent_overrides, name);
467 if (PMC_IS_NULL(result)) {
468 /* Walk and search for the vtable. */
469 const INTVAL num_classes = VTABLE_elements(interp, _class->all_parents);
470 INTVAL i;
472 for (i = 0; i < num_classes; ++i) {
473 /* Get the class. */
474 PMC * const cur_class =
475 VTABLE_get_pmc_keyed_int(interp, _class->all_parents, i);
477 result = Parrot_oo_find_vtable_override_for_class(interp,
478 cur_class, name);
480 if (!PMC_IS_NULL(result))
481 break;
483 if (PMC_IS_NULL(result))
484 result = Parrot_pmc_new(interp, enum_class_Undef);
485 VTABLE_set_pmc_keyed_str(interp, _class->parent_overrides, name, result);
487 if (result->vtable->base_type == enum_class_Undef)
488 result = PMCNULL;
489 return result;
495 =item C<INTVAL Parrot_get_vtable_index(PARROT_INTERP, const STRING *name)>
497 Return index if C<name> is a valid vtable slot name.
499 =cut
503 PARROT_EXPORT
504 INTVAL
505 Parrot_get_vtable_index(PARROT_INTERP, ARGIN(const STRING *name))
507 ASSERT_ARGS(Parrot_get_vtable_index)
508 char * const name_c = Parrot_str_to_cstring(interp, name);
510 /* some of the first "slots" don't have names. skip 'em. */
511 INTVAL low = PARROT_VTABLE_LOW;
512 INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW;
514 while (low < high) {
515 const INTVAL mid = (low + high) / 2;
516 const char * const meth_c = Parrot_vtable_slot_names[mid];
518 const INTVAL cmp = strcmp(name_c, meth_c);
520 if (cmp == 0) {
521 Parrot_str_free_cstring(name_c);
522 return mid;
524 else if (cmp > 0)
525 low = mid + 1;
526 else
527 high = mid;
530 Parrot_str_free_cstring(name_c);
532 return -1;
538 =item C<const char * Parrot_get_vtable_name(PARROT_INTERP, INTVAL idx)>
540 Return the method name at the specified index in the vtable slot array.
541 Use this function when you cannot access Parrot_vtable_slot_names directly.
543 =cut
547 PARROT_EXPORT
548 PARROT_PURE_FUNCTION
549 PARROT_CAN_RETURN_NULL
550 const char *
551 Parrot_get_vtable_name(SHIM_INTERP, INTVAL idx)
553 ASSERT_ARGS(Parrot_get_vtable_name)
555 const INTVAL low = PARROT_VTABLE_LOW;
556 const INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW;
558 PARROT_ASSERT(idx > 0);
560 if (idx < low || idx > high) {
561 return NULL;
564 return Parrot_vtable_slot_names[idx];
570 =item C<static INTVAL fail_if_type_exists(PARROT_INTERP, PMC *name)>
572 This function throws an exception if a PMC or class with the same name *
573 already exists in the global type registry. The global type registry
574 will go away eventually, but this allows the new object metamodel to
575 interact with the old one until it does.
577 =cut
581 static INTVAL
582 fail_if_type_exists(PARROT_INTERP, ARGIN(PMC *name))
584 ASSERT_ARGS(fail_if_type_exists)
585 PMC * const value = (PMC *)VTABLE_get_pointer_keyed(interp, interp->class_hash, name);
587 if (PMC_IS_NULL(value))
588 return 0;
590 switch (VTABLE_type(interp, value)) {
591 case enum_class_NameSpace:
592 return 0;
593 break;
594 case enum_class_Integer:
596 const INTVAL type = VTABLE_get_integer(interp, value);
597 if (type < enum_type_undef) {
598 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
599 "native type with name '%s' already exists - "
600 "can't register Class", data_types[type].name);
602 return type;
604 break;
605 default:
606 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERP_ERROR,
607 "Unrecognized class name PMC type");
608 break;
615 =item C<INTVAL Parrot_oo_register_type(PARROT_INTERP, PMC *name, PMC
616 *_namespace)>
618 This function registers a type in the global registry, first checking if it
619 already exists. The global type registry will go away eventually, but this
620 allows the new object metamodel to interact with the old one until it does.
622 =cut
626 PARROT_WARN_UNUSED_RESULT
627 INTVAL
628 Parrot_oo_register_type(PARROT_INTERP, ARGIN(PMC *name), ARGIN(PMC *_namespace))
630 ASSERT_ARGS(Parrot_oo_register_type)
631 INTVAL type;
632 const INTVAL typeid_exists = fail_if_type_exists(interp, name);
634 PMC * const classobj = VTABLE_get_class(interp, _namespace);
635 if (!PMC_IS_NULL(classobj)) {
636 STRING * const classname = VTABLE_get_string(interp, _namespace);
637 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
638 "Class %Ss already registered!\n",
639 Parrot_str_escape(interp, classname));
642 /* Type doesn't exist, so go ahead and register it. Lock interpreter so
643 * pt_shared_fixup() can safely do a type lookup. */
644 LOCK_INTERPRETER(interp);
646 type = Parrot_pmc_get_new_vtable_index(interp);
649 if (!typeid_exists) {
650 /* set entry in name->type hash */
651 PMC * const classname_hash = interp->class_hash;
652 PMC * const item = Parrot_pmc_new_init_int(interp,
653 enum_class_Integer, type);
654 VTABLE_set_pmc_keyed(interp, classname_hash, name, item);
657 UNLOCK_INTERPRETER(interp);
659 return type;
664 =item C<void mark_object_cache(PARROT_INTERP)>
666 Marks all PMCs in the object method cache as live. This shouldn't strictly be
667 necessary, as they're likely all reachable from namespaces and classes, but
668 it's unlikely to hurt anything except mark phase performance.
670 =cut
674 #define TBL_SIZE_MASK 0x1ff /* x bits 2..10 */
675 #define TBL_SIZE (1 + TBL_SIZE_MASK)
677 void
678 mark_object_cache(PARROT_INTERP)
680 ASSERT_ARGS(mark_object_cache)
681 Caches * const mc = interp->caches;
682 UINTVAL type, entry;
684 if (!mc)
685 return;
687 for (type = 0; type < mc->mc_size; ++type) {
688 if (!mc->idx[type])
689 continue;
691 for (entry = 0; entry < TBL_SIZE; ++entry) {
692 Meth_cache_entry *e = mc->idx[type][entry];
693 while (e) {
694 Parrot_gc_mark_PMC_alive(interp, e->pmc);
695 e = e->next;
704 =item C<void init_object_cache(PARROT_INTERP)>
706 Allocate memory for object cache.
708 =cut
712 void
713 init_object_cache(PARROT_INTERP)
715 ASSERT_ARGS(init_object_cache)
716 Caches * const mc = interp->caches = mem_gc_allocate_zeroed_typed(interp, Caches);
717 mc->idx = NULL;
723 =item C<void destroy_object_cache(PARROT_INTERP)>
725 Destroy the object cache. Loop over all caches and invalidate them. Then
726 free the caches back to the OS.
728 =cut
732 void
733 destroy_object_cache(PARROT_INTERP)
735 ASSERT_ARGS(destroy_object_cache)
736 UINTVAL i;
737 Caches * const mc = interp->caches;
739 /* mc->idx[type][bits] = e; */
740 for (i = 0; i < mc->mc_size; ++i) {
741 if (mc->idx[i])
742 invalidate_type_caches(interp, i);
745 mem_gc_free(interp, mc->idx);
746 mem_gc_free(interp, mc);
752 =item C<static void invalidate_type_caches(PARROT_INTERP, UINTVAL type)>
754 Invalidate the cache of the specified type. Free each entry and then free
755 the entire cache.
757 =cut
761 static void
762 invalidate_type_caches(PARROT_INTERP, UINTVAL type)
764 ASSERT_ARGS(invalidate_type_caches)
765 Caches * const mc = interp->caches;
766 INTVAL i;
768 if (!mc)
769 return;
771 /* is it a valid entry */
772 if (type >= mc->mc_size || !mc->idx[type])
773 return;
775 for (i = 0; i < TBL_SIZE; ++i) {
776 Meth_cache_entry *e = mc->idx[type][i];
777 while (e) {
778 Meth_cache_entry * const next = e->next;
779 mem_gc_free(interp, e);
780 e = next;
784 mem_gc_free(interp, mc->idx[type]);
785 mc->idx[type] = NULL;
791 =item C<static void invalidate_all_caches(PARROT_INTERP)>
793 Invalidate all caches by looping over each cache and calling
794 C<invalidate_type_caches> on them.
796 =cut
800 static void
801 invalidate_all_caches(PARROT_INTERP)
803 ASSERT_ARGS(invalidate_all_caches)
804 int i;
805 for (i = 1; i < interp->n_vtable_max; ++i)
806 invalidate_type_caches(interp, i);
812 =item C<void Parrot_invalidate_method_cache(PARROT_INTERP, STRING *_class)>
814 Clear method cache for the given class. If class is NULL, caches for
815 all classes are invalidated.
817 =cut
821 PARROT_EXPORT
822 void
823 Parrot_invalidate_method_cache(PARROT_INTERP, ARGIN_NULLOK(STRING *_class))
825 ASSERT_ARGS(Parrot_invalidate_method_cache)
826 INTVAL type;
828 /* during interp creation and NCI registration the class_hash
829 * isn't yet up */
830 if (!interp->class_hash)
831 return;
833 if (interp->resume_flag & RESUME_INITIAL)
834 return;
836 if (!_class) {
837 invalidate_all_caches(interp);
838 return;
841 type = Parrot_pmc_get_type_str(interp, _class);
843 if (type == 0)
844 invalidate_all_caches(interp);
845 else if (type > 0)
846 invalidate_type_caches(interp, (UINTVAL)type);
851 =item C<PMC * Parrot_find_method_direct(PARROT_INTERP, PMC *_class, STRING
852 *method_name)>
854 Find a method PMC for a named method, given the class PMC, current
855 interpreter, and name of the method. Don't use a possible method cache.
857 =cut
861 PARROT_EXPORT
862 PARROT_CAN_RETURN_NULL
863 PARROT_WARN_UNUSED_RESULT
864 PMC *
865 Parrot_find_method_direct(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
867 ASSERT_ARGS(Parrot_find_method_direct)
868 PMC * const found = find_method_direct_1(interp, _class, method_name);
870 if (!PMC_IS_NULL(found))
871 return found;
874 if (Parrot_str_equal(interp, method_name, CONST_STRING(interp, "__get_string")))
875 return find_method_direct_1(interp, _class, CONST_STRING(interp, "__get_repr"));
877 return PMCNULL;
883 =item C<PMC * Parrot_find_method_with_cache(PARROT_INTERP, PMC *_class, STRING
884 *method_name)>
886 Find a method PMC for a named method, given the class PMC, current
887 interp, and name of the method.
889 This routine should use the current scope's method cache, if there is
890 one. If not, it creates a new method cache. Or, rather, it will when
891 we've got that bit working. For now it unconditionally goes and looks up
892 the name in the global stash.
894 =cut
898 PARROT_EXPORT
899 PARROT_CAN_RETURN_NULL
900 PARROT_WARN_UNUSED_RESULT
901 PMC *
902 Parrot_find_method_with_cache(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name))
904 ASSERT_ARGS(Parrot_find_method_with_cache)
906 UINTVAL type, bits;
907 Caches *mc;
908 Meth_cache_entry *e;
910 PARROT_ASSERT(method_name != 0);
912 #if DISABLE_METH_CACHE
913 return Parrot_find_method_direct(interp, _class, method_name);
914 #else
916 if (! PObj_constant_TEST(method_name))
917 return Parrot_find_method_direct(interp, _class, method_name);
919 mc = interp->caches;
920 type = _class->vtable->base_type;
921 bits = (((UINTVAL) Buffer_bufstart(method_name)) >> 2) & TBL_SIZE_MASK;
923 if (type >= mc->mc_size) {
924 if (mc->idx) {
925 mc->idx = mem_gc_realloc_n_typed_zeroed(interp, mc->idx,
926 type + 1, mc->mc_size, Meth_cache_entry**);
928 else {
929 mc->idx = mem_gc_allocate_n_zeroed_typed(interp, type + 1, Meth_cache_entry**);
931 mc->mc_size = type + 1;
934 if (mc->idx[type] == NULL) {
935 mc->idx[type] = mem_gc_allocate_n_zeroed_typed(interp,
936 TBL_SIZE, Meth_cache_entry*);
939 e = mc->idx[type][bits];
941 while (e && e->strstart != Buffer_bufstart(method_name)) {
942 e = e->next;
945 if (!e) {
946 /* when here no or no correct entry was at [bits] */
947 /* Use zeroed allocation because find_method_direct can trigger GC */
948 e = mem_gc_allocate_zeroed_typed(interp, Meth_cache_entry);
950 mc->idx[type][bits] = e;
952 e->pmc = Parrot_find_method_direct(interp, _class, method_name);
953 e->next = NULL;
954 e->strstart = Buffer_bufstart(method_name);
957 return e->pmc;
959 #endif
965 =item C<static void debug_trace_find_meth(PARROT_INTERP, const PMC *_class,
966 const STRING *name, const PMC *sub)>
968 Print some information about the search for a sub.
970 =cut
974 #ifdef NDEBUG
975 # define TRACE_FM(i, c, m, sub)
976 #else
977 # define TRACE_FM(i, c, m, sub) \
978 debug_trace_find_meth((i), (c), (m), (sub))
980 static void
981 debug_trace_find_meth(PARROT_INTERP, ARGIN(const PMC *_class),
982 ARGIN(const STRING *name), ARGIN_NULLOK(const PMC *sub))
984 ASSERT_ARGS(debug_trace_find_meth)
985 STRING *class_name;
986 const char *result;
987 Interp *tracer;
989 if (!Interp_trace_TEST(interp, PARROT_TRACE_FIND_METH_FLAG))
990 return;
992 if (PObj_is_class_TEST(_class)) {
993 SLOTTYPE * const class_array = PMC_data_typed(_class, SLOTTYPE *);
994 PMC * const class_name_pmc = get_attrib_num(class_array, PCD_CLASS_NAME);
995 class_name = VTABLE_get_string(interp, class_name_pmc);
997 else
998 class_name = _class->vtable->whoami;
1000 if (sub) {
1001 if (sub->vtable->base_type == enum_class_NCI)
1002 result = "NCI";
1003 else
1004 result = "Sub";
1006 else
1007 result = "no";
1009 tracer = (interp->pdb && interp->pdb->debugger) ?
1010 interp->pdb->debugger :
1011 interp;
1012 Parrot_io_eprintf(tracer, "# find_method class '%Ss' method '%Ss': %s\n",
1013 class_name, name, result);
1016 #endif
1021 =item C<static PMC * find_method_direct_1(PARROT_INTERP, PMC *_class, STRING
1022 *method_name)>
1024 Find the method with the given name in the specified class.
1026 =cut
1030 PARROT_WARN_UNUSED_RESULT
1031 PARROT_CAN_RETURN_NULL
1032 static PMC *
1033 find_method_direct_1(PARROT_INTERP, ARGIN(PMC *_class),
1034 ARGIN(STRING *method_name))
1036 ASSERT_ARGS(find_method_direct_1)
1037 INTVAL i;
1039 PMC * const mro = _class->vtable->mro;
1040 const INTVAL n = VTABLE_elements(interp, mro);
1041 STRING * const methods_str = CONST_STRING(interp, "methods");
1042 STRING * const class_str = CONST_STRING(interp, "class");
1044 for (i = 0; i < n; ++i) {
1045 PMC * const _class = VTABLE_get_pmc_keyed_int(interp, mro, i);
1046 PMC * const ns = VTABLE_get_namespace(interp, _class);
1047 PMC * const class_obj = VTABLE_inspect_str(interp, ns, class_str);
1048 PMC *method = PMCNULL;
1049 PMC * method_hash;
1051 if (PMC_IS_NULL(class_obj))
1052 method_hash = VTABLE_inspect_str(interp, ns, methods_str);
1053 else
1054 method_hash = VTABLE_inspect_str(interp, class_obj, methods_str);
1056 if (!PMC_IS_NULL(method_hash))
1057 method = VTABLE_get_pmc_keyed_str(interp, method_hash, method_name);
1059 if (PMC_IS_NULL(method))
1060 method = VTABLE_get_pmc_keyed_str(interp, ns, method_name);
1062 TRACE_FM(interp, _class, method_name, method);
1064 if (!PMC_IS_NULL(method))
1065 return method;
1068 TRACE_FM(interp, _class, method_name, NULL);
1069 return PMCNULL;
1075 =item C<static PMC* C3_merge(PARROT_INTERP, PMC *merge_list)>
1077 Merge together the MRO of the items in the list.
1079 =cut
1083 PARROT_WARN_UNUSED_RESULT
1084 PARROT_CAN_RETURN_NULL
1085 static PMC*
1086 C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list))
1088 ASSERT_ARGS(C3_merge)
1089 PMC *accepted = PMCNULL;
1090 PMC *result = PMCNULL;
1091 const int list_count = VTABLE_elements(interp, merge_list);
1092 int cand_count = 0;
1093 int i;
1095 /* Try and find something appropriate to add to the MRO - basically, the
1096 * first list head that is not in the tail of all the other lists. */
1097 for (i = 0; i < list_count; ++i) {
1098 PMC * const cand_list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1100 PMC *cand_class;
1101 int reject = 0;
1102 int j;
1104 if (VTABLE_elements(interp, cand_list) == 0)
1105 continue;
1107 cand_class = VTABLE_get_pmc_keyed_int(interp, cand_list, 0);
1108 ++cand_count;
1110 for (j = 0; j < list_count; ++j) {
1111 /* Skip the current list. */
1112 if (j != i) {
1113 /* Is it in the tail? If so, reject. */
1114 PMC * const check_list =
1115 VTABLE_get_pmc_keyed_int(interp, merge_list, j);
1117 const int check_length = VTABLE_elements(interp, check_list);
1118 int k;
1120 for (k = 1; k < check_length; ++k) {
1121 if (VTABLE_get_pmc_keyed_int(interp, check_list, k) ==
1122 cand_class) {
1123 reject = 1;
1124 break;
1130 /* If we didn't reject it, this candidate will do. */
1131 if (!reject) {
1132 accepted = cand_class;
1133 break;
1137 /* If we never found any candidates, return an empty list. */
1138 if (cand_count == 0)
1139 return Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1141 /* If we didn't find anything to accept, error. */
1142 if (PMC_IS_NULL(accepted))
1143 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ILL_INHERIT,
1144 "Could not build C3 linearization: ambiguous hierarchy");
1146 /* Otherwise, remove what was accepted from the merge lists. */
1147 for (i = 0; i < list_count; ++i) {
1149 PMC * const list = VTABLE_get_pmc_keyed_int(interp, merge_list, i);
1150 const INTVAL sublist_count = VTABLE_elements(interp, list);
1151 INTVAL j;
1153 for (j = 0; j < sublist_count; ++j) {
1154 if (VTABLE_get_pmc_keyed_int(interp, list, j) == accepted) {
1155 VTABLE_delete_keyed_int(interp, list, j);
1156 break;
1161 /* Need to merge what remains of the list, then put what was accepted on
1162 * the start of the list, and we're done. */
1163 result = C3_merge(interp, merge_list);
1164 VTABLE_unshift_pmc(interp, result, accepted);
1166 return result;
1172 =item C<PMC* Parrot_ComputeMRO_C3(PARROT_INTERP, PMC *_class)>
1174 Computes the C3 linearization for the given class. C3 is an algorithm to
1175 compute the method resolution order (MRO) of a class that is inheriting
1176 from multiple parent classes (multiple inheritance). C3 was first described
1177 by Barrett et al at:
1179 F<http://192.220.96.201/dylan/linearization-oopsla96.html>
1181 =cut
1185 PARROT_EXPORT
1186 PARROT_WARN_UNUSED_RESULT
1187 PARROT_CAN_RETURN_NULL
1188 PMC*
1189 Parrot_ComputeMRO_C3(PARROT_INTERP, ARGIN(PMC *_class))
1191 ASSERT_ARGS(Parrot_ComputeMRO_C3)
1193 PMC * const immediate_parents = VTABLE_inspect_str(interp, _class, CONST_STRING(interp, "parents"));
1194 PMC *merge_list;
1195 PMC *result;
1197 INTVAL i;
1198 INTVAL parent_count;
1200 /* Now get immediate parents list. */
1201 if (PMC_IS_NULL(immediate_parents))
1202 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_METHOD_NOT_FOUND,
1203 "Failed to get parents list from class!");
1205 parent_count = VTABLE_elements(interp, immediate_parents);
1207 if (parent_count == 0) {
1208 /* No parents - MRO just contains this class. */
1209 result = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1210 VTABLE_push_pmc(interp, result, _class);
1211 return result;
1214 /* Otherwise, need to do merge. For that, need linearizations of all of
1215 * our parents added to the merge list. */
1216 merge_list = PMCNULL;
1217 for (i = 0; i < parent_count; ++i) {
1218 PMC * const lin = Parrot_ComputeMRO_C3(interp,
1219 VTABLE_get_pmc_keyed_int(interp, immediate_parents, i));
1221 if (PMC_IS_NULL(lin))
1222 return PMCNULL;
1224 /* instantiated lazily */
1225 if (PMC_IS_NULL(merge_list))
1226 merge_list = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
1228 VTABLE_push_pmc(interp, merge_list, lin);
1231 /* Finally, need list of direct parents on the end of the merge list, then
1232 * we can merge. */
1233 VTABLE_push_pmc(interp, merge_list, immediate_parents);
1234 result = C3_merge(interp, merge_list);
1236 if (PMC_IS_NULL(result))
1237 return PMCNULL;
1239 /* Merged result needs this class on the start, and then we're done. */
1240 VTABLE_unshift_pmc(interp, result, _class);
1242 return result;
1248 =item C<void Parrot_ComposeRole(PARROT_INTERP, PMC *role, PMC *exclude, int
1249 got_exclude, PMC *alias, int got_alias, PMC *methods_hash, PMC *roles_list)>
1251 Used by the Class and Object PMCs internally to compose a role into either of
1252 them. The C<role> parameter is the role that we are composing into the class
1253 or role. C<methods_hash> is the hash of method names to invokable PMCs that
1254 contains the methods the class or role has. C<roles_list> is the list of roles
1255 the the class or method does.
1257 The C<role> parameter is only dealt with by its external interface. Whether
1258 this routine is usable by any other object system implemented in Parrot very
1259 much depends on how closely the role composition semantics they want are to
1260 the default implementation.
1262 =cut
1266 PARROT_EXPORT
1267 void
1268 Parrot_ComposeRole(PARROT_INTERP, ARGIN(PMC *role),
1269 ARGIN(PMC *exclude), int got_exclude,
1270 ARGIN(PMC *alias), int got_alias,
1271 ARGIN(PMC *methods_hash), ARGIN(PMC *roles_list))
1273 ASSERT_ARGS(Parrot_ComposeRole)
1274 PMC *methods;
1275 PMC *methods_iter;
1276 PMC *roles_of_role;
1277 PMC *proposed_add_methods;
1279 INTVAL roles_of_role_count;
1280 INTVAL i;
1282 /* Check we have not already composed the role; if so, just ignore it. */
1283 INTVAL roles_count = VTABLE_elements(interp, roles_list);
1285 for (i = 0; i < roles_count; ++i)
1286 if (VTABLE_get_pmc_keyed_int(interp, roles_list, i) == role)
1287 return;
1289 /* Get the methods from the role. */
1290 Parrot_pcc_invoke_method_from_c_args(interp, role, CONST_STRING(interp, "methods"), "->P", &methods);
1292 if (PMC_IS_NULL(methods))
1293 return;
1295 /* We need to check for conflicts before we do the composition. We
1296 * put each method that would be OK to add into a proposal list, and
1297 * bail out right away if we find a problem. */
1298 proposed_add_methods = Parrot_pmc_new(interp, enum_class_Hash);
1299 methods_iter = VTABLE_get_iter(interp, methods);
1301 while (VTABLE_get_bool(interp, methods_iter)) {
1302 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1303 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp, methods,
1304 method_name);
1306 /* Need to find the name we'll check for a conflict on. */
1307 int excluded = 0;
1309 /* Check if it's in the exclude list. */
1310 if (got_exclude) {
1311 const int exclude_count = VTABLE_elements(interp, exclude);
1313 for (i = 0; i < exclude_count; ++i) {
1314 const STRING * const check =
1315 VTABLE_get_string_keyed_int(interp, exclude, i);
1317 if (Parrot_str_equal(interp, check, method_name)) {
1318 excluded = 1;
1319 break;
1324 /* If we weren't excluded... */
1325 if (!excluded) {
1326 /* Is there a method with this name already in the class? */
1328 if (VTABLE_exists_keyed_str(interp, methods_hash, method_name)) {
1329 /* Conflicts with something already in the class, unless it's a
1330 * multi-method. */
1331 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, method_name);
1332 if (PMC_IS_NULL(cur_entry) || !VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1333 Parrot_ex_throw_from_c_args(interp, NULL,
1334 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1335 "A conflict occurred during role composition "
1336 "due to method '%S'.", method_name);
1339 /* What about a conflict with ourslef? */
1340 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1341 method_name))
1342 /* Something very weird is going on. */
1343 Parrot_ex_throw_from_c_args(interp, NULL,
1344 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1345 "A conflict occurred during role composition;"
1346 " the method '%S' from the role managed to conflict "
1347 "with itself somehow.", method_name);
1349 /* If we got here, no conflicts! Add method to the "to compose"
1350 * list. */
1351 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1352 method_name, cur_method);
1355 /* Now see if we've got an alias. */
1356 if (got_alias && VTABLE_exists_keyed_str(interp, alias, method_name)) {
1357 /* Got one. Get name to alias it to. */
1358 STRING * const alias_name = VTABLE_get_string_keyed_str(interp,
1359 alias, method_name);
1361 /* Is there a method with this name already in the class? If it's
1362 * not a multi-method, error. */
1363 if (VTABLE_exists_keyed_str(interp, methods_hash, alias_name)) {
1364 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, alias_name);
1365 if (PMC_IS_NULL(cur_entry) || !VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1366 /* Conflicts with something already in the class. */
1367 Parrot_ex_throw_from_c_args(interp, NULL,
1368 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1369 "A conflict occurred during role composition"
1370 " due to the aliasing of '%S' to '%S'.",
1371 method_name, alias_name);
1374 /* What about a conflict with ourslef? */
1375 if (VTABLE_exists_keyed_str(interp, proposed_add_methods,
1376 alias_name))
1377 Parrot_ex_throw_from_c_args(interp, NULL,
1378 EXCEPTION_ROLE_COMPOSITION_METHOD_CONFLICT,
1379 "A conflict occurred during role composition"
1380 " due to the aliasing of '%S' to '%S' (role already has"
1381 " a method '%S').", method_name, alias_name, alias_name);
1383 /* If we get here, no conflicts! Add method to the "to compose"
1384 * list with its alias. */
1385 VTABLE_set_pmc_keyed_str(interp, proposed_add_methods,
1386 alias_name, cur_method);
1390 /* If we get here, we detected no conflicts. Go ahead and compose the
1391 * methods. */
1392 methods_iter = VTABLE_get_iter(interp, proposed_add_methods);
1394 while (VTABLE_get_bool(interp, methods_iter)) {
1395 /* Get current method and its name. */
1396 STRING * const method_name = VTABLE_shift_string(interp, methods_iter);
1397 PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp,
1398 proposed_add_methods, method_name);
1400 /* Add it to the methods of the class. */
1401 PMC * const cur_entry = VTABLE_get_pmc_keyed_str(interp, methods_hash, method_name);
1402 if (VTABLE_isa(interp, cur_method, CONST_STRING(interp, "MultiSub"))) {
1403 /* The thing we're adding is a multi-sub, but is the thing in the
1404 * class already a multi-sub? */
1405 if (!PMC_IS_NULL(cur_entry) && VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub"))) {
1406 /* Class already has a multi-sub; need to merge our methods into it. */
1407 const INTVAL num_subs = VTABLE_elements(interp, cur_method);
1408 INTVAL j;
1409 for (j = 0; j < num_subs; ++j)
1410 VTABLE_push_pmc(interp, cur_entry, VTABLE_get_pmc_keyed_int(interp,
1411 cur_method, j));
1413 else {
1414 /* It's not, and we didn't conflict so must be no entry. Just stick it in. */
1415 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1418 else {
1419 /* Are we adding into a multi-sub? */
1420 if (!PMC_IS_NULL(cur_entry) && VTABLE_isa(interp, cur_entry, CONST_STRING(interp, "MultiSub")))
1421 VTABLE_push_pmc(interp, cur_entry, cur_method);
1422 else
1423 VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method);
1427 /* Add this role to the roles list. */
1428 VTABLE_push_pmc(interp, roles_list, role);
1429 ++roles_count;
1431 /* As a result of composing this role, we will also now do the roles
1432 * that it did itself. Note that we already have the correct methods
1433 * as roles "flatten" the methods they get from other roles into their
1434 * own method list. */
1435 Parrot_pcc_invoke_method_from_c_args(interp, role, CONST_STRING(interp, "roles"), "->P", &roles_of_role);
1436 roles_of_role_count = VTABLE_elements(interp, roles_of_role);
1438 for (i = 0; i < roles_of_role_count; ++i) {
1439 /* Only add if we don't already have it in the list. */
1440 PMC * const cur_role = VTABLE_get_pmc_keyed_int(interp,
1441 roles_of_role, i);
1442 INTVAL j;
1444 for (j = 0; j < roles_count; ++j) {
1445 if (VTABLE_get_pmc_keyed_int(interp, roles_list, j) == cur_role) {
1446 /* We ain't be havin' it. */
1447 VTABLE_push_pmc(interp, roles_list, cur_role);
1456 =back
1458 =head1 SEE ALSO
1460 F<include/parrot/oo.h>, F<include/parrot/oo_private.h>,
1461 F<docs/pdds/pdd15_objects.pod>.
1463 =cut
1468 * Local variables:
1469 * c-file-style: "parrot"
1470 * End:
1471 * vim: expandtab shiftwidth=4: