# New Ticket Created by chromatic # Please include the string: [perl #50002] # in the subject line of all future correspondence about this issue. # <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=50002 >
This large patch merges the necessary functions from src/objects.c into src/oo.c. The latter is the new PDD 15 file. I've taken the liberty of removing unused code from src/objects.c, which gives a very nice: $ diffstat objects.patch MANIFEST | 2 config/gen/makefiles/root.in | 5 src/objects.c | 2536 ----------------------------------------- src/oo.c | 1829 ++++++++++++++++++++++++++++++- 4 files changed, 1828 insertions(+), 2544 deletions(-) You need to reconfigure and rebuild after this so as not to include a latent src/objects.o file in your libparrot. Because this patch is large and in charge, I'd like to hear some success reports from a couple of non-x86 and non-Linux platforms before I commit it. -- c
=== MANIFEST ================================================================== --- MANIFEST (revision 25030) +++ MANIFEST (local) @@ -882,7 +882,6 @@ include/parrot/misc.h [main]include include/parrot/mmd.h [main]include include/parrot/nci.h [main]include -include/parrot/objects.h [main]include include/parrot/oo.h [main]include include/parrot/oo_private.h [main]include include/parrot/op.h [main]include @@ -2820,7 +2819,6 @@ src/misc.c [] src/mmd.c [] src/nci_test.c [] -src/objects.c [] src/oo.c [] src/ops/bit.ops [] src/ops/cmp.ops [] === config/gen/makefiles/root.in ================================================================== --- config/gen/makefiles/root.in (revision 25030) +++ config/gen/makefiles/root.in (local) @@ -438,7 +438,6 @@ $(SRC_DIR)/mmd$(O) \ $(SRC_DIR)/nci$(O) \ $(SRC_DIR)/oo$(O) \ - $(SRC_DIR)/objects$(O) \ $(SRC_DIR)/packfile$(O) \ $(SRC_DIR)/packout$(O) \ $(SRC_DIR)/pic_jit$(O) \ @@ -609,7 +608,6 @@ $(SRC_DIR)/pmc.str \ $(SRC_DIR)/oo.str \ $(SRC_DIR)/scheduler.str \ - $(SRC_DIR)/objects.str \ $(SRC_DIR)/spf_render.str \ $(SRC_DIR)/spf_vtable.str \ $(CLASS_STR_FILES) @@ -1127,8 +1125,6 @@ $(SRC_DIR)/builtin$(O) : $(GENERAL_H_FILES) $(SRC_DIR)/builtin.str -$(SRC_DIR)/objects$(O) : $(GENERAL_H_FILES) $(SRC_DIR)/objects.str - $(SRC_DIR)/packfile$(O) : $(GENERAL_H_FILES) $(PF_DIR)/pf_items$(O) : $(GENERAL_H_FILES) @@ -1997,7 +1993,6 @@ src/nci.c \ src/nci_test.c \ src/null_config.c \ - src/objects.c \ src/oo.c \ src/packdump.c \ src/packfile.c \ === src/oo.c ================================================================== --- src/oo.c (revision 25030) +++ src/oo.c (local) @@ -1,5 +1,5 @@ /* -Copyright (C) 2007, The Perl Foundation. +Copyright (C) 2007-2008, The Perl Foundation. $Id$ =head1 NAME @@ -27,6 +27,100 @@ /* HEADERIZER HFILE: include/parrot/oo.h */ +/* HEADERIZER BEGIN: static */ + +static INTVAL attr_str_2_num(PARROT_INTERP, + ARGIN(PMC *object), + ARGIN(STRING *attr)) + __attribute__nonnull__(1) + __attribute__nonnull__(2) + __attribute__nonnull__(3); + +PARROT_WARN_UNUSED_RESULT +PARROT_CAN_RETURN_NULL +static PMC* C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list)) + __attribute__nonnull__(1) + __attribute__nonnull__(2); + +static void create_deleg_pmc_vtable(PARROT_INTERP, + ARGIN(PMC *_class), + int full) + __attribute__nonnull__(1) + __attribute__nonnull__(2); + +static void debug_trace_find_meth(PARROT_INTERP, + ARGIN(const PMC *_class), + ARGIN(const STRING *name), + ARGIN_NULLOK(const PMC *sub)) + __attribute__nonnull__(1) + __attribute__nonnull__(2) + __attribute__nonnull__(3); + +static void do_initcall(PARROT_INTERP, + ARGIN_NULLOK(PMC* _class), + ARGIN_NULLOK(PMC *object), + ARGIN_NULLOK(PMC *init)) + __attribute__nonnull__(1); + +static void fail_if_exist(PARROT_INTERP, ARGIN(PMC *name)) + __attribute__nonnull__(1) + __attribute__nonnull__(2); + +PARROT_WARN_UNUSED_RESULT +PARROT_CAN_RETURN_NULL +static PMC * find_method_direct_1(PARROT_INTERP, + ARGIN(PMC *_class), + ARGIN(STRING *method_name)) + __attribute__nonnull__(1) + __attribute__nonnull__(2) + __attribute__nonnull__(3); + +PARROT_WARN_UNUSED_RESULT +PARROT_CAN_RETURN_NULL +static PMC* get_init_meth(PARROT_INTERP, + ARGIN(PMC *_class), + ARGIN(STRING *prop_str), + ARGOUT(STRING **meth_str)) + __attribute__nonnull__(1) + __attribute__nonnull__(2) + __attribute__nonnull__(3) + __attribute__nonnull__(4) + FUNC_MODIFIES(*meth_str); + +static void instantiate_object(PARROT_INTERP, + ARGMOD(PMC *object), + ARGIN_NULLOK(PMC *init)) + __attribute__nonnull__(1) + __attribute__nonnull__(2) + FUNC_MODIFIES(*object); + +static void invalidate_all_caches(PARROT_INTERP) + __attribute__nonnull__(1); + +static void invalidate_type_caches(PARROT_INTERP, UINTVAL type) + __attribute__nonnull__(1); + +static void parrot_class_register(PARROT_INTERP, + ARGIN(PMC *name), + ARGIN(PMC *new_class), + ARGIN_NULLOK(PMC *parent), + ARGIN(PMC *mro)) + __attribute__nonnull__(1) + __attribute__nonnull__(2) + __attribute__nonnull__(3) + __attribute__nonnull__(5); + +static void rebuild_attrib_stuff(PARROT_INTERP, ARGIN(PMC *_class)) + __attribute__nonnull__(1) + __attribute__nonnull__(2); + +PARROT_WARN_UNUSED_RESULT +static INTVAL register_type(PARROT_INTERP, ARGIN(PMC *name)) + __attribute__nonnull__(1) + __attribute__nonnull__(2); + +/* HEADERIZER END: static */ + /* =item C<void Parrot_oo_extract_methods_from_namespace> @@ -300,6 +394,1739 @@ /* +=item C<INTVAL Parrot_get_vtable_index> + +Return index if C<name> is a valid vtable slot name. + +=cut + +*/ + +PARROT_API +INTVAL +Parrot_get_vtable_index(PARROT_INTERP, ARGIN(const STRING *name)) +{ + char * const name_c = string_to_cstring(interp, name); + + /* some of the first "slots" don't have names. skip 'em. */ + INTVAL low = PARROT_VTABLE_LOW; + INTVAL high = NUM_VTABLE_FUNCTIONS + PARROT_VTABLE_LOW; + + while (low < high) { + const INTVAL mid = (low + high) / 2; + const char * const meth_c = Parrot_vtable_slot_names[mid]; + + /* RT#45965 slot_names still have __ in front */ + const INTVAL cmp = strcmp(name_c, meth_c + 2); + + if (cmp == 0) { + string_cstring_free(name_c); + return mid; + } + else if (cmp > 0) + low = mid + 1; + else + high = mid; + } + + string_cstring_free(name_c); + + return -1; +} + +/* + +=item C<static PMC* find_vtable_meth_ns> + +Return Sub PMC if a method with the vtable name exists in ns + +=cut + +*/ + +PARROT_WARN_UNUSED_RESULT +PARROT_CAN_RETURN_NULL +static PMC* +find_vtable_meth_ns(PARROT_INTERP, ARGIN(PMC *ns), INTVAL vtable_index) +{ + return VTABLE_get_pmc_keyed_int(interp, ns, vtable_index); +} + +/* + +=item C<PMC* Parrot_find_vtable_meth> + +Given pmc, run through its mro looking for the meth vtable method. +Return the vtable method PMC if found. + +=cut + +*/ + +PARROT_API +PARROT_CAN_RETURN_NULL +PMC* +Parrot_find_vtable_meth(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(STRING *meth)) +{ + INTVAL i, n; + PMC *ns, *mro; + PMC *_class = pmc; + + /* Get index in Parrot_vtable_slot_names[]. */ + const INTVAL vtable_index = Parrot_get_vtable_index(interp, meth); + + if (vtable_index == -1) + return PMCNULL; + + /* Get class. */ + if (PObj_is_object_TEST(pmc)) + _class = GET_CLASS(PMC_data_typed(pmc, Buffer), pmc); + + /* Get MRO and iterate over it to find method with a matching + vtable index or double-underscored name. */ + mro = _class->vtable->mro; + n = VTABLE_elements(interp, mro); + + for (i = 0; i < n; ++i) { + _class = VTABLE_get_pmc_keyed_int(interp, mro, i); + ns = VTABLE_pmc_namespace(interp, _class); + + if (!PMC_IS_NULL(ns)) { + PMC * const res = find_vtable_meth_ns(interp, ns, vtable_index); + + if (!PMC_IS_NULL(res)) + return res; + } + } + + /* If we get here, method is not overridden in the class. */ + return PMCNULL; +} + +/* + +=item C<STRING* readable_name> + +Given a String or Key PMC return the STRING* representation + +RT#45967 this function, key_set_to_string, and the key PMC get_repr should be consolidated + +=cut + +*/ + +PARROT_API +PARROT_WARN_UNUSED_RESULT +PARROT_CANNOT_RETURN_NULL +STRING* +readable_name(PARROT_INTERP, ARGIN(PMC *name)) +{ + STRING *join_on; + PMC *array; + + if (name->vtable->base_type == enum_class_String) + return VTABLE_get_string(interp, name); + + join_on = CONST_STRING(interp, ";"); + array = pmc_new(interp, enum_class_ResizableStringArray); + + PARROT_ASSERT(name->vtable->base_type == enum_class_Key); + + while (name) { + VTABLE_push_string(interp, array, key_string(interp, name)); + name = key_next(interp, name); + } + + return string_join(interp, join_on, array); +} + +/* + +=item C<static void fail_if_exist> + +Throws an exception if a PMC or class with the same name already exists. + +RT#45969 uses global class registry + +=cut + +*/ + +static void +fail_if_exist(PARROT_INTERP, ARGIN(PMC *name)) +{ + INTVAL type; + + PMC * const classname_hash = interp->class_hash; + PMC * const type_pmc = (PMC *)VTABLE_get_pointer_keyed(interp, + classname_hash, name); + if (PMC_IS_NULL(type_pmc) || + type_pmc->vtable->base_type == enum_class_NameSpace) + type = 0; + else + type = VTABLE_get_integer(interp, type_pmc); + + if (type > enum_type_undef) { + /* RT#45971 get printable name */ + real_exception(interp, NULL, INVALID_OPERATION, + "Class %Ss already registered!\n", + VTABLE_get_string(interp, name)); + } + + if (type < enum_type_undef) + real_exception(interp, NULL, INVALID_OPERATION, + "native type with name '%s' already exists - " + "can't register Class", data_types[type].name); +} + +/* + +=item C<static void rebuild_attrib_stuff> + +Take the class and completely rebuild the attribute stuff for +it. Horribly destructive, and definitely not a good thing to do if +there are instantiated objects for the class + +=cut + +*/ + +static void +rebuild_attrib_stuff(PARROT_INTERP, ARGIN(PMC *_class)) +{ + INTVAL attr_count, cur_offset, n_class, n_mro, offset; + PMC *attr_offset_hash, *mro, *attribs; + SLOTTYPE *class_slots; + +#ifndef NDEBUG + PMC * const orig_class = _class; +#endif + + /* attrib count isn't set yet, a GC caused by concat could + * corrupt data under construction + */ + Parrot_block_DOD(interp); + + class_slots = PMC_data_typed(_class, SLOTTYPE *); + attr_offset_hash = pmc_new(interp, enum_class_Hash); + set_attrib_num(_class, class_slots, PCD_ATTRIBUTES, attr_offset_hash); + + mro = _class->vtable->mro; + n_mro = VTABLE_elements(interp, mro); + + /* walk from oldest parent down to n_class == 0 which is this class */ + cur_offset = 0; + + for (n_class = n_mro - 1; n_class >= 0; --n_class) { + STRING *classname; + + _class = VTABLE_get_pmc_keyed_int(interp, mro, n_class); + + /* this Class isa PMC - no attributes there */ + if (!PObj_is_class_TEST(_class)) + continue; + + class_slots = PMC_data_typed(_class, SLOTTYPE *); + classname = VTABLE_get_string(interp, + get_attrib_num(class_slots, PCD_CLASS_NAME)); + attribs = get_attrib_num(class_slots, PCD_CLASS_ATTRIBUTES); + attr_count = VTABLE_elements(interp, attribs); + + if (attr_count) { + STRING * const partial_name = string_concat(interp, classname, + string_from_cstring(interp, "\0", 1), 0); + + for (offset = 0; offset < attr_count; offset++) { + STRING * const attr_name = VTABLE_get_string_keyed_int(interp, attribs, offset); + STRING * const full_name = string_concat(interp, partial_name, attr_name, 0); + + /* store this attribute with short and full name */ + + VTABLE_set_integer_keyed_str(interp, attr_offset_hash, + attr_name, cur_offset); + VTABLE_set_integer_keyed_str(interp, attr_offset_hash, + full_name, cur_offset); + cur_offset++; + } + } + } + +#ifndef NDEBUG + PARROT_ASSERT(_class == orig_class); +#endif + + /* And note the totals */ + CLASS_ATTRIB_COUNT(_class) = cur_offset; + Parrot_unblock_DOD(interp); +} + +/* + +=item C<static void create_deleg_pmc_vtable> + +Create a vtable that dispatches either to the contained PMC in the first +attribute (deleg_pmc) or to an overridden method (delegate), depending +on the existence of the method for this class. + +=cut + +*/ + +static void +create_deleg_pmc_vtable(PARROT_INTERP, ARGIN(PMC *_class), int full) +{ + int i; + const char *meth; + STRING meth_str; + DECL_CONST_CAST; + + PMC * const vtable_pmc = get_attrib_num(PMC_data_typed(_class, + SLOTTYPE *), PCD_OBJECT_VTABLE); + VTABLE * const vtable = (VTABLE *)PMC_struct_val(vtable_pmc); + VTABLE * const ro_vtable = vtable->ro_variant_vtable; + VTABLE * const deleg_pmc_vtable = interp->vtables[enum_class_deleg_pmc]; + VTABLE * const object_vtable = interp->vtables[enum_class_Object]; + VTABLE * const ro_object_vtable = object_vtable->ro_variant_vtable; + VTABLE * const delegate_vtable = interp->vtables[enum_class_delegate]; + + memset(&meth_str, 0, sizeof (meth_str)); + + meth_str.encoding = Parrot_fixed_8_encoding_ptr; + meth_str.charset = Parrot_default_charset_ptr; + + for (i = 0; (meth = Parrot_vtable_slot_names[i]) != NULL; ++i) { + if (!*meth) + continue; + + /* strip underscores from method name */ + meth_str.strstart = (char *)const_cast(meth + 2); + meth_str.strlen = meth_str.bufused = strlen(meth) - 2; + meth_str.hashval = 0; + + if (!PMC_IS_NULL(Parrot_find_vtable_meth(interp, _class, &meth_str))) { + /* the method exists; keep the ParrotObject delegate vtable slot */ + ((void **)vtable)[i] = ((void**)object_vtable)[i]; + if (ro_vtable) + ((void **)ro_vtable)[i] = ((void**)ro_object_vtable)[i]; + } + else if (full) { + /* + * the method doesn't exist; put in the deleg_pmc vtable, + * but only if ParrotObject hasn't overridden the method + */ + if (((void **)delegate_vtable)[i] == ((void**)object_vtable)[i]) { + if (ro_vtable) + ((void **)ro_vtable)[i] = ((void**)deleg_pmc_vtable)[i]; + ((void **)vtable)[i] = ((void**)deleg_pmc_vtable)[i]; + } + else { + ((void **)vtable)[i] = ((void**)object_vtable)[i]; + if (ro_vtable) + ((void **)ro_vtable)[i] = ((void**)ro_object_vtable)[i]; + + } + } + } +} + +/* + +=item C<const char* Parrot_MMD_method_name> + +Return the method name for the given MMD enum. + +=cut + +*/ + +PARROT_API +PARROT_PURE_FUNCTION +PARROT_CAN_RETURN_NULL +const char* +Parrot_MMD_method_name(SHIM_INTERP, INTVAL idx) +{ + PARROT_ASSERT(idx >= 0); + + if (idx >= MMD_USER_FIRST) + return NULL; + + return Parrot_mmd_func_names[idx]; +} + +/* + +=item C<INTVAL Parrot_MMD_method_idx> + +Return the MMD function number for method name or -1 on failure. + +RT#45973 allow dynamic expansion at runtime. + +=cut + +*/ + +PARROT_API +PARROT_PURE_FUNCTION +INTVAL +Parrot_MMD_method_idx(SHIM_INTERP, ARGIN(const char *name)) +{ + INTVAL i; + + for (i = 0; i < MMD_USER_FIRST; ++i) { + if (strcmp(Parrot_mmd_func_names[i], name) == 0) + return i; + } + + return -1; +} + + +/* + +=item C<PMC * Parrot_single_subclass> + +Subclass a class. Single parent class, nice and straightforward. If +C<child_class> is C<NULL>, this is an anonymous subclass we're creating, +function. + +=cut + +*/ + +PARROT_API +PARROT_WARN_UNUSED_RESULT +PARROT_CANNOT_RETURN_NULL +PMC * +Parrot_single_subclass(PARROT_INTERP, ARGIN(PMC *base_class), ARGIN_NULLOK(PMC *name)) +{ + PMC *child_class, *parents, *temp_pmc, *mro; + SLOTTYPE *child_class_array; + int parent_is_class; + + /* Set the classname, if we have one */ + if (!PMC_IS_NULL(name)) { + fail_if_exist(interp, name); + } + else { + /* RT#45975 not really threadsafe but good enough for now */ + static int anon_count; + STRING * const child_class_name = + Parrot_sprintf_c(interp, "%c%canon_%d", 0, 0, ++anon_count); + name = pmc_new(interp, enum_class_String); + VTABLE_set_string_native(interp, name, child_class_name); + } + + /* ParrotClass is the baseclass anyway, so build just a new class */ + if (base_class == interp->vtables[enum_class_Class]->pmc_class) + return pmc_new_init(interp, enum_class_Class, name); + + parent_is_class = PObj_is_class_TEST(base_class); + child_class = pmc_new(interp, enum_class_Class); + + /* Hang an array off the data pointer */ + set_attrib_array_size(child_class, PCD_MAX); + child_class_array = PMC_data_typed(child_class, SLOTTYPE *); + set_attrib_flags(child_class); + + /* We will have five entries in this array */ + + /* We have the same number of attributes as our parent */ + CLASS_ATTRIB_COUNT(child_class) = parent_is_class ? + CLASS_ATTRIB_COUNT(base_class) : 0; + + /* Our parent class array has a single member in it */ + parents = pmc_new(interp, enum_class_ResizablePMCArray); + + VTABLE_set_integer_native(interp, parents, 1); + VTABLE_set_pmc_keyed_int(interp, parents, 0, base_class); + + set_attrib_num(child_class, child_class_array, PCD_PARENTS, parents); + set_attrib_num(child_class, child_class_array, PCD_CLASS_NAME, name); + + /* Our mro list is a clone of our parent's mro list, + * with our self unshifted onto the beginning */ + mro = VTABLE_clone(interp, base_class->vtable->mro); + VTABLE_unshift_pmc(interp, mro, child_class); + + /* But we have no attributes of our own. Yet */ + temp_pmc = pmc_new(interp, enum_class_ResizablePMCArray); + set_attrib_num(child_class, child_class_array, PCD_CLASS_ATTRIBUTES, + temp_pmc); + + parrot_class_register(interp, name, child_class, base_class, mro); + rebuild_attrib_stuff(interp, child_class); + + if (!parent_is_class) { + /* we append one attribute to hold the PMC */ + Parrot_add_attribute(interp, child_class, + CONST_STRING(interp, "__value")); + /* + * then create a vtable derived from ParrotObject and + * deleg_pmc - the ParrotObject vtable is already built + */ + create_deleg_pmc_vtable(interp, child_class, 1); + } + else { + /* + * if any parent isa PMC, then still individual vtables might + * be overridden in this subclass + */ + int i, any_pmc_parent; + + const int n = VTABLE_elements(interp, mro); + any_pmc_parent = 0; + + /* 0 = this, 1 = parent (handled above), 2 = grandpa */ + for (i = 2; i < n; ++i) { + const PMC * const parent = VTABLE_get_pmc_keyed_int(interp, mro, i); + if (!PObj_is_class_TEST(parent)) { + any_pmc_parent = 1; + break; + } + } + if (any_pmc_parent) + create_deleg_pmc_vtable(interp, child_class, 0); + } + + return child_class; +} + +/* + +=item C<PMC * Parrot_class_lookup> + +Looks for the class named C<class_name> and returns it if it exists. +Otherwise it returns C<PMCNULL>. + +=cut + +*/ + +PARROT_API +PARROT_CAN_RETURN_NULL +PARROT_WARN_UNUSED_RESULT +PMC * +Parrot_class_lookup(PARROT_INTERP, ARGIN(STRING *class_name)) +{ + const INTVAL type = pmc_type(interp, class_name); + PMC *pmc; + + if (type <= 0) + return PMCNULL; + + pmc = interp->vtables[type]->pmc_class; + PARROT_ASSERT(pmc); + return pmc; +} + +/* + +=item C<PMC * Parrot_class_lookup_p> + +Looks for the class named C<class_name> and returns it if it exists. +Otherwise it returns C<PMCNULL>. + +=cut + +*/ + +PARROT_CAN_RETURN_NULL +PARROT_WARN_UNUSED_RESULT +PMC * +Parrot_class_lookup_p(PARROT_INTERP, ARGIN(PMC *class_name)) +{ + const INTVAL type = pmc_type_p(interp, class_name); + PMC *pmc; + + if (type <= 0) + return PMCNULL; + + pmc = interp->vtables[type]->pmc_class; + PARROT_ASSERT(pmc); + return pmc; +} + +/* + +=item C<static INTVAL register_type> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +PARROT_WARN_UNUSED_RESULT +static INTVAL +register_type(PARROT_INTERP, ARGIN(PMC *name)) +{ + INTVAL type; + PMC *classname_hash, *item; + + /* so pt_shared_fixup() can safely do a type lookup */ + LOCK_INTERPRETER(interp); + classname_hash = interp->class_hash; + + type = interp->n_vtable_max++; + + /* Have we overflowed the table? */ + if (type >= interp->n_vtable_alloced) + parrot_realloc_vtables(interp); + + /* set entry in name->type hash */ + item = pmc_new(interp, enum_class_Integer); + PMC_int_val(item) = type; + + VTABLE_set_pmc_keyed(interp, classname_hash, name, item); + UNLOCK_INTERPRETER(interp); + + return type; +} + +/* + +=item C<static void parrot_class_register> + +This is the way to register a new Parrot class as an instantiable +type. Doing this involves putting it in the class hash, setting its +vtable so that the C<init> method initializes objects of the class rather than +the class itself, and adding it to the interpreter's base type table so +you can create a new C<foo> in PASM like this: C<new Px, foo>. + +=cut + +*/ + +static void +parrot_class_register(PARROT_INTERP, ARGIN(PMC *name), + ARGIN(PMC *new_class), ARGIN_NULLOK(PMC *parent), ARGIN(PMC *mro)) +{ + VTABLE *new_vtable, *parent_vtable; + PMC *vtable_pmc, *ns, *top; + const INTVAL new_type = register_type(interp, name); + + /* Build a new vtable for this class + * The child class PMC gets the vtable of its parent class or + * a ParrotClass vtable + */ + if (parent && PObj_is_class_TEST(parent)) + parent_vtable = parent->vtable; + else + parent_vtable = new_class->vtable; + + new_vtable = Parrot_clone_vtable(interp, parent_vtable); + + /* Set the vtable's type to the newly allocated type */ + new_vtable->base_type = new_type; + + /* And cache our class PMC in the vtable so we can find it later */ + new_vtable->pmc_class = new_class; + new_vtable->mro = mro; + + if (parent_vtable->ro_variant_vtable) + new_vtable->ro_variant_vtable = + Parrot_clone_vtable(interp, parent_vtable->ro_variant_vtable); + + /* Reset the init method to our instantiation method */ + new_vtable->init = Parrot_instantiate_object; + new_vtable->init_pmc = Parrot_instantiate_object_init; + new_class->vtable = new_vtable; + + /* Put our new vtable in the global table */ + interp->vtables[new_type] = new_vtable; + + /* check if we already have a NameSpace */ + top = CONTEXT(interp->ctx)->current_namespace; + ns = VTABLE_get_pmc_keyed(interp, top, name); + + /* RT#45979 nested, use current as base ? */ + if (PMC_IS_NULL(ns)) { + /* RT#45983 try HLL namespace too */ + top = Parrot_get_ctx_HLL_namespace(interp); + ns = VTABLE_get_pmc_keyed(interp, top, name); + } + + if (PMC_IS_NULL(ns)) { + ns = pmc_new(interp, enum_class_NameSpace); + VTABLE_set_pmc_keyed(interp, top, name, ns); + } + + /* attach namespace to vtable */ + new_vtable->_namespace = ns; + + if (new_vtable->ro_variant_vtable) { + VTABLE * const ro_vt = new_vtable->ro_variant_vtable; + + ro_vt->base_type = new_vtable->base_type; + ro_vt->pmc_class = new_vtable->pmc_class; + ro_vt->mro = new_vtable->mro; + ro_vt->_namespace = new_vtable->_namespace; + } + + /* + * prepare object vtable - again that of the parent or + * a ParrotObject vtable + */ + if (parent && PObj_is_class_TEST(parent)) { + vtable_pmc = + get_attrib_num((SLOTTYPE *)PMC_data(parent), PCD_OBJECT_VTABLE); + parent_vtable = (VTABLE *)PMC_struct_val(vtable_pmc); + } + else + parent_vtable = interp->vtables[enum_class_Object]; + + new_vtable = Parrot_clone_vtable(interp, parent_vtable); + + if (parent_vtable->ro_variant_vtable) + new_vtable->ro_variant_vtable = + Parrot_clone_vtable(interp, parent_vtable->ro_variant_vtable); + + new_vtable->base_type = new_type; + new_vtable->mro = mro; + new_vtable->pmc_class = new_class; + + set_attrib_num(new_class, (SLOTTYPE*)PMC_data(new_class), PCD_OBJECT_VTABLE, + vtable_pmc = constant_pmc_new(interp, enum_class_VtableCache)); + PMC_struct_val(vtable_pmc) = new_vtable; + + /* attach namespace to object vtable too */ + new_vtable->_namespace = ns; + + if (new_vtable->ro_variant_vtable) { + VTABLE * const ro_vt = new_vtable->ro_variant_vtable; + + ro_vt->base_type = new_vtable->base_type; + ro_vt->pmc_class = new_vtable->pmc_class; + ro_vt->mro = new_vtable->mro; + ro_vt->_namespace = new_vtable->_namespace; + } +} + +/* + +=item C<static PMC* get_init_meth> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +PARROT_WARN_UNUSED_RESULT +PARROT_CAN_RETURN_NULL +static PMC* +get_init_meth(PARROT_INTERP, ARGIN(PMC *_class), + ARGIN(STRING *prop_str), ARGOUT(STRING **meth_str)) +{ + STRING *meth; + HashBucket *b; + PMC *props, *ns, *method; + + *meth_str = NULL; +#if 0 + PMC *prop; + prop = VTABLE_getprop(interp, _class, prop_str); + if (!VTABLE_defined(interp, prop)) + return PMCNULL; + meth = VTABLE_get_string(interp, prop); +#else + props = PMC_metadata(_class); + if (!props) + return PMCNULL; + b = parrot_hash_get_bucket(interp, + (Hash*) PMC_struct_val(props), prop_str); + if (!b) + return PMCNULL; + meth = PMC_str_val((PMC*) b->value); +#endif + + *meth_str = meth; + ns = VTABLE_pmc_namespace(interp, _class); + method = VTABLE_get_pmc_keyed_str(interp, ns, meth); + + return method; +} + + +/* + +=item C<static void do_initcall> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +static void +do_initcall(PARROT_INTERP, ARGIN_NULLOK(PMC* _class), ARGIN_NULLOK(PMC *object), + ARGIN_NULLOK(PMC *init)) +{ + PMC * const classsearch_array = _class->vtable->mro; + INTVAL i, nparents; + + /* + * 1) if class has a CONSTRUCT property run it on the object + * no redispatch + * + * RT#45985 isn't CONSTRUCT for creating new objects? + */ + STRING *meth_str; + PMC *meth = get_init_meth(interp, _class, + CONST_STRING(interp, "CONSTRUCT"), &meth_str); + int default_meth; + + if (!PMC_IS_NULL(meth)) { + if (init) + Parrot_run_meth_fromc_args(interp, meth, + object, meth_str, "vP", init); + else + Parrot_run_meth_fromc_args(interp, meth, + object, meth_str, "v"); + } + /* + * 2. if class has a BUILD property call it for all classes + * in reverse search order - this class last. + * + * Note: mro contains this class as first element + */ + nparents = VTABLE_elements(interp, classsearch_array); + + for (i = nparents - 1; i >= 0; --i) { + PMC * const parent_class = + VTABLE_get_pmc_keyed_int(interp, classsearch_array, i); + /* if it's a PMC, we put one PMC of that type into + * the attribute slot #0 and call init() on that PMC + */ + if (!PObj_is_class_TEST(parent_class)) { + PMC *attr, *next_parent; + SLOTTYPE *obj_data; + + /* + * but only if init isn't inherited + * or rather just on the last non-class parent + */ + PARROT_ASSERT(i >= 1); + next_parent = VTABLE_get_pmc_keyed_int(interp, + classsearch_array, i - 1); + if (!PObj_is_class_TEST(next_parent)) + continue; + + attr = pmc_new_noinit(interp, parent_class->vtable->base_type); + obj_data = PMC_data_typed(object, SLOTTYPE *); + set_attrib_num(object, obj_data, 0, attr); + VTABLE_init(interp, attr); + continue; + } + meth = get_init_meth(interp, parent_class, + CONST_STRING(interp, "BUILD"), &meth_str); + /* no method found and no BUILD property set? */ + if (PMC_IS_NULL(meth) && meth_str == NULL) { + PMC *ns; + INTVAL vtable_index; + + /* use __init or __init_pmc (depending on if an argument was passed) + * as fallback constructor method, if it exists */ + if (init) + meth_str = CONST_STRING(interp, "init_pmc"); + else + meth_str = CONST_STRING(interp, "init"); + + ns = VTABLE_pmc_namespace(interp, parent_class); + + /* can't use find_method, it walks mro */ + vtable_index = Parrot_get_vtable_index(interp, meth_str); + meth = find_vtable_meth_ns(interp, ns, vtable_index); + default_meth = 1; + } + else + default_meth = 0; + + if (!PMC_IS_NULL(meth)) { + if (init) + Parrot_run_meth_fromc_args(interp, meth, + object, meth_str, "vP", init); + else + Parrot_run_meth_fromc_args(interp, meth, + object, meth_str, "v"); + } + else if (meth_str != NULL && + string_length(interp, meth_str) != 0 && !default_meth) { + real_exception(interp, NULL, METH_NOT_FOUND, + "Class BUILD method ('%Ss') not found", meth_str); + } + } +} + +/* + +=item C<void Parrot_instantiate_object_init> + +Creates a Parrot object. Takes a passed-in class PMC that has sufficient +information to describe the layout of the object and makes the object. + +=cut + +*/ + +PARROT_API +void +Parrot_instantiate_object_init(PARROT_INTERP, ARGIN(PMC *object), ARGIN(PMC *init)) +{ + instantiate_object(interp, object, init); +} + +/* + +=item C<void Parrot_instantiate_object> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +PARROT_API +void +Parrot_instantiate_object(PARROT_INTERP, ARGMOD(PMC *object)) +{ + instantiate_object(interp, object, NULL); +} + +/* + +=item C<static void instantiate_object> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +static void +instantiate_object(PARROT_INTERP, ARGMOD(PMC *object), ARGIN_NULLOK(PMC *init)) +{ + SLOTTYPE *new_object_array; + INTVAL attrib_count, i; + + PMC * const _class = object->vtable->pmc_class; + /* + * put in the real vtable + */ + PMC * const vtable_pmc = get_attrib_num((SLOTTYPE *)PMC_data(_class), + PCD_OBJECT_VTABLE); + object->vtable = (VTABLE *)PMC_struct_val(vtable_pmc); + + /* Grab the attribute count from the class */ + attrib_count = CLASS_ATTRIB_COUNT(_class); + + /* Build the array that hangs off the new object */ + /* First presize it */ + set_attrib_array_size(object, attrib_count); + new_object_array = PMC_data_typed(object, SLOTTYPE *); + + /* fill with PMCNULL, so that access doesn't segfault */ + for (i = 0; i < attrib_count; ++i) + set_attrib_num(object, new_object_array, i, PMCNULL); + + /* turn marking on */ + set_attrib_flags(object); + + /* We are an object now */ + PObj_is_object_SET(object); + + /* We really ought to call the class init routines here... + * this assumes that an object isa delegate + */ + do_initcall(interp, _class, object, init); +} + +/* + +=item C<PMC * Parrot_remove_parent> + +This currently does nothing but return C<PMCNULL>. + +=cut + +*/ + +PARROT_API +PARROT_IGNORABLE_RESULT +PARROT_CAN_RETURN_NULL +PMC * +Parrot_remove_parent(PARROT_INTERP, ARGIN(PMC *removed_class), + ARGIN(PMC *existing_class)) +{ + UNUSED(interp); + UNUSED(removed_class); + UNUSED(existing_class); + + return PMCNULL; +} + +/* + +=item C<void mark_object_cache> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +void +mark_object_cache(PARROT_INTERP) +{ + UNUSED(interp); +} + +/* + +=item C<void init_object_cache> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +void +init_object_cache(PARROT_INTERP) +{ + Caches * const mc = interp->caches = mem_allocate_zeroed_typed(Caches); + mc->idx = NULL; +} + +/* + +=item C<void destroy_object_cache> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +void +destroy_object_cache(PARROT_INTERP) +{ + UINTVAL i; + Caches * const mc = interp->caches; + + /* mc->idx[type][bits] = e; */ + for (i = 0; i < mc->mc_size; i++) { + if (mc->idx[i]) + invalidate_type_caches(interp, i); + } + + mem_sys_free(mc->idx); + mem_sys_free(mc); +} + +#define TBL_SIZE_MASK 0x1ff /* x bits 2..10 */ +#define TBL_SIZE (1 + TBL_SIZE_MASK) + +/* + +=item C<static void invalidate_type_caches> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +static void +invalidate_type_caches(PARROT_INTERP, UINTVAL type) +{ + Caches * const mc = interp->caches; + INTVAL i; + + if (!mc) + return; + + /* is it a valid entry */ + if (type >= mc->mc_size || !mc->idx[type]) + return; + + for (i = 0; i < TBL_SIZE; ++i) { + Meth_cache_entry *e; + for (e = mc->idx[type][i]; e;) { + Meth_cache_entry * const next = e->next; + mem_sys_free(e); + e = next; + } + } + + mem_sys_free(mc->idx[type]); + mc->idx[type] = NULL; +} + +/* + +=item C<static void invalidate_all_caches> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +static void +invalidate_all_caches(PARROT_INTERP) +{ + UINTVAL i; + for (i = 1; i < (UINTVAL)interp->n_vtable_max; ++i) + invalidate_type_caches(interp, i); +} + +/* + +=item C<void Parrot_invalidate_method_cache> + +Clear method cache for the given class. If class is NULL, caches for +all classes are invalidated. + +=cut + +*/ + +PARROT_API +void +Parrot_invalidate_method_cache(PARROT_INTERP, ARGIN_NULLOK(STRING *_class), ARGIN(STRING *meth)) +{ + INTVAL type; + + /* during interp creation and NCI registration the class_hash + * isn't yet up */ + if (!interp->class_hash) + return; + + if (interp->resume_flag & RESUME_INITIAL) + return; + + if (!_class) { + invalidate_all_caches(interp); + return; + } + + type = pmc_type(interp, _class); + + if (type < 0) + return; + + if (type == 0) { + invalidate_all_caches(interp); + return; + } + + invalidate_type_caches(interp, (UINTVAL)type); +} + +/* + * quick'n'dirty method cache + * RT#45987: use a hash if method_name is not constant + * i.e. from obj.$Sreg(args) + * If this hash is implemented mark it during DOD + */ + +/* + +=item C<PMC * Parrot_find_method_direct> + +Find a method PMC for a named method, given the class PMC, current +interpreter, and name of the method. Don't use a possible method cache. + +=cut + +*/ + +PARROT_API +PARROT_CAN_RETURN_NULL +PARROT_WARN_UNUSED_RESULT +PMC * +Parrot_find_method_direct(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name)) +{ + PMC * const found = find_method_direct_1(interp, _class, method_name); + STRING *s1, *s2; + + if (!PMC_IS_NULL(found)) + return found; + + s1 = CONST_STRING(interp, "__get_string"); + s2 = CONST_STRING(interp, "__get_repr"); + + if (string_equal(interp, method_name, s1) == 0) + return find_method_direct_1(interp, _class, s2); + + return PMCNULL; +} + +/* + +=item C<PMC * Parrot_find_method_with_cache> + +Find a method PMC for a named method, given the class PMC, current +interp, and name of the method. + +This routine should use the current scope's method cache, if there is +one. If not, it creates a new method cache. Or, rather, it will when +we've got that bit working. For now it unconditionally goes and looks up +the name in the global stash. + +=cut + +*/ + +PARROT_API +PARROT_CAN_RETURN_NULL +PARROT_WARN_UNUSED_RESULT +PMC * +Parrot_find_method_with_cache(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *method_name)) +{ + UINTVAL type, bits; + + Caches *mc; + Meth_cache_entry *e, *old; + + PARROT_ASSERT(method_name != 0); + +#if DISABLE_METH_CACHE + return Parrot_find_method_direct(interp, _class, method_name); +#endif + + if (! PObj_constant_TEST(method_name)) + return Parrot_find_method_direct(interp, _class, method_name); + + mc = interp->caches; + type = _class->vtable->base_type; + bits = (((UINTVAL) method_name->strstart) >> 2) & TBL_SIZE_MASK; + + if (type >= mc->mc_size) { + if (mc->idx) { + mc->idx = (Meth_cache_entry ***)mem_sys_realloc_zeroed(mc->idx, + sizeof (Meth_cache_entry ***) * (type + 1), + sizeof (Meth_cache_entry ***) * mc->mc_size); + } + else { + mc->idx = (Meth_cache_entry ***)mem_sys_allocate_zeroed( + sizeof (Meth_cache_entry ***) * (type + 1)); + } + mc->mc_size = type + 1; + } + + if (!mc->idx[type]) { + mc->idx[type] = (Meth_cache_entry **)mem_sys_allocate_zeroed( + sizeof (Meth_cache_entry *) * TBL_SIZE); + } + + e = mc->idx[type][bits]; + old = NULL; + + while (e && e->strstart != method_name->strstart) { + old = e; + e = e->next; + } + + if (!e) { + /* when here no or no correct entry was at [bits] */ + e = mem_allocate_typed(Meth_cache_entry); + + if (old) + old->next = e; + else + mc->idx[type][bits] = e; + + e->pmc = Parrot_find_method_direct(interp, _class, method_name); + e->next = NULL; + e->strstart = method_name->strstart; + } + + return e->pmc; +} + +/* + +=item C<static void debug_trace_find_meth> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +#ifdef NDEBUG +# define TRACE_FM(i, c, m, sub) +#else +# define TRACE_FM(i, c, m, sub) \ + debug_trace_find_meth(i, c, m, sub) +static void +debug_trace_find_meth(PARROT_INTERP, ARGIN(const PMC *_class), + ARGIN(const STRING *name), ARGIN_NULLOK(const PMC *sub)) +{ + STRING *class_name; + const char *result; + Interp *tracer; + + if (!Interp_trace_TEST(interp, PARROT_TRACE_FIND_METH_FLAG)) + return; + if (PObj_is_class_TEST(_class)) { + SLOTTYPE * const class_array = PMC_data_typed(_class, SLOTTYPE *); + PMC *const class_name_pmc = get_attrib_num(class_array, PCD_CLASS_NAME); + class_name = PMC_str_val(class_name_pmc); + } + else + class_name = _class->vtable->whoami; + if (sub) { + if (sub->vtable->base_type == enum_class_NCI) + result = "NCI"; + else + result = "Sub"; + } + else + result = "no"; + tracer = interp->debugger ? interp->debugger : interp; + PIO_eprintf(tracer, + "# find_method class '%Ss' method '%Ss': %s\n", + class_name, name, result); +} + +#endif + +/* + +=item C<static PMC * find_method_direct_1> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +PARROT_WARN_UNUSED_RESULT +PARROT_CAN_RETURN_NULL +static PMC * +find_method_direct_1(PARROT_INTERP, ARGIN(PMC *_class), + ARGIN(STRING *method_name)) +{ + INTVAL i; + + PMC * const mro = _class->vtable->mro; + const INTVAL n = VTABLE_elements(interp, mro); + for (i = 0; i < n; ++i) { + PMC* method, *ns; + + _class = VTABLE_get_pmc_keyed_int(interp, mro, i); + ns = VTABLE_pmc_namespace(interp, _class); + method = VTABLE_get_pmc_keyed_str(interp, ns, method_name); + TRACE_FM(interp, _class, method_name, method); + if (!PMC_IS_NULL(method)) { + return method; + } + } + TRACE_FM(interp, _class, method_name, NULL); + return PMCNULL; +} + +/* + +=item C<INTVAL Parrot_add_attribute> + +Adds the attribute C<attr> to the class. + + Life is ever so much easier if a class keeps its attributes at the + end of the attribute array, since we don't have to insert and + reorder attributes. Inserting's no big deal, especially since we're + going to break horribly if you insert into a class that's been + subclassed, but it'll do for now. + +=cut + +*/ + +PARROT_API +INTVAL +Parrot_add_attribute(PARROT_INTERP, ARGIN(PMC *_class), ARGIN(STRING *attr)) +{ + STRING *full_attr_name; + SLOTTYPE * const class_array = (SLOTTYPE *)PMC_data(_class); + STRING * const class_name = VTABLE_get_string(interp, + get_attrib_num(class_array, PCD_CLASS_NAME)); + PMC * const attr_array = get_attrib_num(class_array, + PCD_CLASS_ATTRIBUTES); + PMC * const attr_hash = get_attrib_num(class_array, PCD_ATTRIBUTES); + INTVAL idx = VTABLE_elements(interp, attr_array); + + VTABLE_set_integer_native(interp, attr_array, idx + 1); + VTABLE_set_string_keyed_int(interp, attr_array, idx, attr); + + full_attr_name = string_concat(interp, class_name, + string_from_cstring(interp, "\0", 1), 0); + + full_attr_name = string_concat(interp, full_attr_name, attr, 0); + + /* RT#45989 escape NUL char */ + if (VTABLE_exists_keyed_str(interp, attr_hash, full_attr_name)) { + real_exception(interp, NULL, 1, + "Attribute '%Ss' already exists", full_attr_name); + } + + /* + * RT#45993 check if someone is trying to add attributes to a parent class + * while there are already child class attrs + */ + idx = CLASS_ATTRIB_COUNT(_class)++; + VTABLE_set_integer_keyed_str(interp, attr_hash, attr, idx); + VTABLE_set_integer_keyed_str(interp, attr_hash, full_attr_name, idx); + + return idx; +} + +/* ************************************************************************ */ +/* ********* BELOW HERE IS NEW PPD15 IMPLEMENTATION RELATED STUFF ********* */ +/* ************************************************************************ */ + +/* + +=item C<static PMC* C3_merge> + +RT#48260: Not yet documented!!! + +=cut + +*/ + +PARROT_WARN_UNUSED_RESULT +PARROT_CAN_RETURN_NULL +static PMC* +C3_merge(PARROT_INTERP, ARGIN(PMC *merge_list)) +{ + PMC *result = pmc_new(interp, enum_class_ResizablePMCArray); + const int list_count = VTABLE_elements(interp, merge_list); + int cand_count = 0; + int i; + PMC *accepted = PMCNULL; + + /* Try and find something appropriate to add to the MRO - basically, the + * first list head that is not in the tail of all the other lists. */ + for (i = 0; i < list_count; i++) { + PMC * const cand_list = VTABLE_get_pmc_keyed_int(interp, merge_list, i); + + PMC *cand_class; + int reject = 0; + int j; + + if (VTABLE_elements(interp, cand_list) == 0) + continue; + + cand_class = VTABLE_get_pmc_keyed_int(interp, cand_list, 0); + cand_count++; + + for (j = 0; j < list_count; j++) { + /* Skip the current list. */ + if (j != i) { + /* Is it in the tail? If so, reject. */ + PMC * const check_list = VTABLE_get_pmc_keyed_int(interp, merge_list, j); + const int check_length = VTABLE_elements(interp, check_list); + int k; + + for (k = 1; k < check_length; k++) { + if (VTABLE_get_pmc_keyed_int(interp, check_list, k) == + cand_class) { + reject = 1; + break; + } + } + } + } + + /* If we didn't reject it, this candidate will do. */ + if (!reject) { + accepted = cand_class; + break; + } + } + + /* If we never found any candidates, return an empty list. */ + if (cand_count == 0) + return pmc_new(interp, enum_class_ResizablePMCArray); + + /* If we didn't find anything to accept, error. */ + if (PMC_IS_NULL(accepted)) + real_exception(interp, NULL, ILL_INHERIT, + "Could not build C3 linearization: ambiguous hierarchy"); + + /* Otherwise, remove what was accepted from the merge lists. */ + for (i = 0; i < list_count; i++) { + int j; + + PMC * const list = VTABLE_get_pmc_keyed_int(interp, merge_list, i); + const int list_count = VTABLE_elements(interp, list); + + for (j = 0; j < list_count; j++) { + if (VTABLE_get_pmc_keyed_int(interp, list, j) == accepted) { + VTABLE_delete_keyed_int(interp, list, j); + break; + } + } + } + + /* Need to merge what remains of the list, then put what was accepted on + * the start of the list, and we're done. */ + result = C3_merge(interp, merge_list); + VTABLE_unshift_pmc(interp, result, accepted); + + return result; +} + +/* + +=item C<PMC* Parrot_ComputeMRO_C3> + +Computes the C3 linearization for the given class. + +=cut + +*/ + +PARROT_API +PARROT_WARN_UNUSED_RESULT +PARROT_CAN_RETURN_NULL +PMC* +Parrot_ComputeMRO_C3(PARROT_INTERP, ARGIN(PMC *_class)) +{ + PMC *result; + PMC * const merge_list = pmc_new(interp, enum_class_ResizablePMCArray); + PMC *immediate_parents; + int i, parent_count; + + /* Now get immediate parents list. */ + Parrot_PCCINVOKE(interp, _class, + CONST_STRING(interp, "parents"), + "->P", &immediate_parents); + + if (immediate_parents == NULL) + real_exception(interp, NULL, METH_NOT_FOUND, + "Failed to get parents list from class!"); + + parent_count = VTABLE_elements(interp, immediate_parents); + + if (parent_count == 0) { + /* No parents - MRO just contains this class. */ + result = pmc_new(interp, enum_class_ResizablePMCArray); + VTABLE_push_pmc(interp, result, _class); + return result; + } + + /* Otherwise, need to do merge. For that, need linearizations of all of + * our parents added to the merge list. */ + for (i = 0; i < parent_count; i++) { + PMC *lin; + lin = Parrot_ComputeMRO_C3(interp, + VTABLE_get_pmc_keyed_int(interp, immediate_parents, i)); + + if (PMC_IS_NULL(lin)) + return PMCNULL; + + VTABLE_push_pmc(interp, merge_list, lin); + } + + /* Finally, need list of direct parents on the end of the merge list, then + * we can merge. */ + VTABLE_push_pmc(interp, merge_list, immediate_parents); + result = C3_merge(interp, merge_list); + + if (PMC_IS_NULL(result)) + return PMCNULL; + + /* Merged result needs this class on the start, and then we're done. */ + VTABLE_unshift_pmc(interp, result, _class); + + return result; +} + +/* + +=item C<void Parrot_ComposeRole> + +Used by the Class and Object PMCs internally to compose a role into either of +them. The C<role> parameter is the role that we are composing into the class +or role. C<methods_hash> is the hash of method names to invokable PMCs that +contains the methods the class or role has. C<roles_list> is the list of roles +the the class or method does. + +The C<role> parameter is only dealt with by its external interface. Whether +this routine is usable by any other object system implemented in Parrot very +much depends on how closely the role composition semantics they want are to +the default implementation. + +=cut + +*/ + +PARROT_API +void +Parrot_ComposeRole(PARROT_INTERP, ARGIN(PMC *role), + ARGIN(PMC *exclude), int got_exclude, + ARGIN(PMC *alias), int got_alias, + ARGIN(PMC *methods_hash), ARGIN(PMC *roles_list)) +{ + PMC *methods; + PMC *methods_iter; + PMC *roles_of_role; + PMC *proposed_add_methods; + + int i, roles_of_role_count; + + /* Check we have not already composed the role; if so, just ignore it. */ + int roles_count = VTABLE_elements(interp, roles_list); + + for (i = 0; i < roles_count; i++) { + if (VTABLE_get_pmc_keyed_int(interp, roles_list, i) == role) + return; + } + + /* Get the methods from the role. */ + Parrot_PCCINVOKE(interp, role, + CONST_STRING(interp, "methods"), "->P", &methods); + + if (PMC_IS_NULL(methods)) + return; + + /* We need to check for conflicts before we do the composition. We + * put each method that would be OK to add into a proposal list, and + * bail out right away if we find a problem. */ + proposed_add_methods = pmc_new(interp, enum_class_Hash); + methods_iter = VTABLE_get_iter(interp, methods); + + while (VTABLE_get_bool(interp, methods_iter)) { + STRING * const method_name = VTABLE_shift_string(interp, methods_iter); + PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp, methods, method_name); + + /* Need to find the name we'll check for a conflict on. */ + int excluded = 0; + + /* Check if it's in the exclude list. */ + if (got_exclude) { + const int exclude_count = VTABLE_elements(interp, exclude); + + for (i = 0; i < exclude_count; i++) { + const STRING * const check = VTABLE_get_string_keyed_int(interp, exclude, i); + + if (string_equal(interp, check, method_name) == 0) { + excluded = 1; + break; + } + } + } + + /* If we weren't excluded... */ + if (!excluded) { + /* Is there a method with this name already in the class? + * RT#45999 multi-method handling. */ + if (VTABLE_exists_keyed_str(interp, methods_hash, method_name)) { + /* Conflicts with something already in the class. */ + real_exception(interp, NULL, ROLE_COMPOSITION_METH_CONFLICT, + "A conflict occurred during role composition " + "due to method '%S'.", method_name); + return; + } + + /* What about a conflict with ourslef? */ + if (VTABLE_exists_keyed_str(interp, proposed_add_methods, + method_name)) { + /* Something very weird is going on. */ + real_exception(interp, NULL, ROLE_COMPOSITION_METH_CONFLICT, + "A conflict occurred during role composition;" + " the method '%S' from the role managed to conflict " + "with itself somehow.", method_name); + return; + } + + /* If we got here, no conflicts! Add method to the "to compose" + * list. */ + VTABLE_set_pmc_keyed_str(interp, proposed_add_methods, + method_name, cur_method); + } + + /* Now see if we've got an alias. */ + if (got_alias && VTABLE_exists_keyed_str(interp, alias, method_name)) { + /* Got one. Get name to alias it to. */ + STRING * const alias_name = VTABLE_get_string_keyed_str(interp, + alias, method_name); + + /* Is there a method with this name already in the class? + * RT#45999: multi-method handling. */ + if (VTABLE_exists_keyed_str(interp, methods_hash, alias_name)) { + /* Conflicts with something already in the class. */ + real_exception(interp, NULL, ROLE_COMPOSITION_METH_CONFLICT, + "A conflict occurred during role composition" + " due to the aliasing of '%S' to '%S'.", + method_name, alias_name); + return; + } + + /* What about a conflict with ourslef? */ + if (VTABLE_exists_keyed_str(interp, proposed_add_methods, + alias_name)) { + real_exception(interp, NULL, ROLE_COMPOSITION_METH_CONFLICT, + "A conflict occurred during role composition" + " due to the aliasing of '%S' to '%S' (role already has" + " a method '%S').", + method_name, alias_name, alias_name); + return; + } + + /* If we got here, no conflicts! Add method to the "to compose" + * list with its alias. */ + VTABLE_set_pmc_keyed_str(interp, proposed_add_methods, + alias_name, cur_method); + } + } + + /* If we get here, we detected no conflicts. Go ahead and compose the + * methods. */ + methods_iter = VTABLE_get_iter(interp, proposed_add_methods); + + while (VTABLE_get_bool(interp, methods_iter)) { + /* Get current method and its name. */ + STRING * const method_name = VTABLE_shift_string(interp, methods_iter); + PMC * const cur_method = VTABLE_get_pmc_keyed_str(interp, proposed_add_methods, + method_name); + + /* Add it to the methods of the class. */ + VTABLE_set_pmc_keyed_str(interp, methods_hash, method_name, cur_method); + } + + /* Add this role to the roles list. */ + VTABLE_push_pmc(interp, roles_list, role); + roles_count++; + + /* As a result of composing this role, we will also now do the roles + * that it did itself. Note that we already have the correct methods + * as roles "flatten" the methods they get from other roles into their + * own method list. */ + Parrot_PCCINVOKE(interp, role, + CONST_STRING(interp, "roles"), "->P", &roles_of_role); + roles_of_role_count = VTABLE_elements(interp, roles_of_role); + + for (i = 0; i < roles_of_role_count; i++) { + /* Only add if we don't already have it in the list. */ + PMC * const cur_role = VTABLE_get_pmc_keyed_int(interp, roles_of_role, i); + int j; + + for (j = 0; j < roles_count; j++) { + if (VTABLE_get_pmc_keyed_int(interp, roles_list, j) == cur_role) { + /* We ain't be havin' it. */ + VTABLE_push_pmc(interp, roles_list, cur_role); + } + } + } +} + +/* + =back =head1 SEE ALSO