# 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