fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / pmc / class.pmc
blobe1c129a7fb18fb92d9fb2c3a66903cf8ded1dece
1 /*
2 Copyright (C) 2001-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/pmc/class.pmc - defines a class
9 =head1 DESCRIPTION
11 This class implements the Class PMC, as outlined in
12 F<docs/pdds/pdd15_objects.pod>.
14 Class is not derived from any other PMC.
16 =head2 Structure
18 The Class PMC structure (C<Parrot_Class>) consists of twelve items:
20 =over 4
22 =item C<id>
24 The type number of the PMC.
26 =item C<name>
28 The name of the class -- a STRING.
29 An empty STRING is allocated during initialization.
31 =item C<namespace>
33 The namespace the class is associated with, if any.
34 A Null PMC is allocated during initialization.
36 =item C<instantiated>
38 A flag denoting whether this class has been instantiated since last
39 modification.  A native integer with value zero is allocated during
40 initialization.
42 =item C<parents>
44 An array of immediate parent classes.
45 An empty ResizablePMCArray PMC is allocated during initialization.
47 =item C<all_parents>
49 A cached array of ourself and all parent classes, in method resolution
50 order (MRO). A ResizablePMCArray PMC is allocated during initialization,
51 and is populated with the current class.
53 =item C<roles>
55 An array of the roles this class has been composed from.
56 An empty ResizablePMCArray PMC is allocated during initialization.
58 =item C<methods>
60 A directory of method names and method bodies this class provides.
61 An empty Hash PMC is allocated during initialization.
63 =item C<vtable_overrides>
65 A directory of vtable function names and method bodies this class overrides.
66 An empty Hash PMC is allocated during initialization.
68 =item C<attrib_metadata>
70 A directory of attribute names and attribute metadata this class contains.
71 An empty Hash PMC is allocated during initialization.
73 =item C<attrib_index>
75 A lookup table for attributes in this class and parents.
76 A Null PMC is allocated during initialization.
78 =item C<attrib_cache>
80 A cache of visible attribute names to attribute indexes.
81 A Null PMC is allocated during initialization.
83 =item C<resolve_method>
85 A list of method names the class provides used for name conflict resolution.
86 An empty ResizablePMCArray PMC is allocated during initialization.
88 =cut
92 #define PARROT_IN_OBJECTS_C /* To get the vtable.h imports we want. */
93 #include "parrot/oo_private.h"
94 #include "pmc/pmc_object.h"
95 #include "pmc/pmc_namespace.h"
97 /* HEADERIZER HFILE: none */
98 /* HEADERIZER BEGIN: static */
99 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
101 static void build_attrib_index(PARROT_INTERP, ARGIN(PMC *self))
102         __attribute__nonnull__(1)
103         __attribute__nonnull__(2);
105 static int cache_class_attribs(PARROT_INTERP,
106     ARGIN(PMC *cur_class),
107     ARGIN(PMC *attrib_index),
108     ARGIN(PMC *cache),
109     int cur_index)
110         __attribute__nonnull__(1)
111         __attribute__nonnull__(2)
112         __attribute__nonnull__(3)
113         __attribute__nonnull__(4);
115 static void calculate_mro(PARROT_INTERP,
116     ARGIN(PMC *SELF),
117     INTVAL num_parents)
118         __attribute__nonnull__(1)
119         __attribute__nonnull__(2);
121 static void init_class_from_hash(PARROT_INTERP,
122     ARGMOD(PMC *self),
123     ARGIN_NULLOK(PMC *info))
124         __attribute__nonnull__(1)
125         __attribute__nonnull__(2)
126         FUNC_MODIFIES(*self);
128 static void initialize_parents(PARROT_INTERP,
129     ARGIN(PMC *object),
130     ARGIN(PMC *all_parents))
131         __attribute__nonnull__(1)
132         __attribute__nonnull__(2)
133         __attribute__nonnull__(3);
135 static void initialize_parents_pmc(PARROT_INTERP,
136     ARGIN(PMC *object),
137     ARGIN(PMC *all_parents),
138     ARGIN(PMC *init))
139         __attribute__nonnull__(1)
140         __attribute__nonnull__(2)
141         __attribute__nonnull__(3)
142         __attribute__nonnull__(4);
144 PARROT_CANNOT_RETURN_NULL
145 static STRING * make_class_name(PARROT_INTERP, ARGIN(PMC *SELF))
146         __attribute__nonnull__(1)
147         __attribute__nonnull__(2);
149 #define ASSERT_ARGS_build_attrib_index __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
150        PARROT_ASSERT_ARG(interp) \
151     , PARROT_ASSERT_ARG(self))
152 #define ASSERT_ARGS_cache_class_attribs __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
153        PARROT_ASSERT_ARG(interp) \
154     , PARROT_ASSERT_ARG(cur_class) \
155     , PARROT_ASSERT_ARG(attrib_index) \
156     , PARROT_ASSERT_ARG(cache))
157 #define ASSERT_ARGS_calculate_mro __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
158        PARROT_ASSERT_ARG(interp) \
159     , PARROT_ASSERT_ARG(SELF))
160 #define ASSERT_ARGS_init_class_from_hash __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
161        PARROT_ASSERT_ARG(interp) \
162     , PARROT_ASSERT_ARG(self))
163 #define ASSERT_ARGS_initialize_parents __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
164        PARROT_ASSERT_ARG(interp) \
165     , PARROT_ASSERT_ARG(object) \
166     , PARROT_ASSERT_ARG(all_parents))
167 #define ASSERT_ARGS_initialize_parents_pmc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
168        PARROT_ASSERT_ARG(interp) \
169     , PARROT_ASSERT_ARG(object) \
170     , PARROT_ASSERT_ARG(all_parents) \
171     , PARROT_ASSERT_ARG(init))
172 #define ASSERT_ARGS_make_class_name __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
173        PARROT_ASSERT_ARG(interp) \
174     , PARROT_ASSERT_ARG(SELF))
175 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
176 /* HEADERIZER END: static */
180 =item C<static int cache_class_attribs(PARROT_INTERP, PMC *cur_class, PMC
181 *attrib_index, PMC *cache, int cur_index)>
183 =cut
187 static int
188 cache_class_attribs(PARROT_INTERP,
189         ARGIN(PMC *cur_class), ARGIN(PMC *attrib_index),
190         ARGIN(PMC *cache), int cur_index)
192     ASSERT_ARGS(cache_class_attribs)
193     /* The attribute metadata hash. */
194     Parrot_Class_attributes * const class_info = PARROT_CLASS(cur_class);
195     PMC          * const attribs     = class_info->attrib_metadata;
196     PMC          * const iter        = VTABLE_get_iter(interp, attribs);
198     /* Build a string representing the fully qualified class name. */
199     /* Retrieve the fully qualified class name for the class. */
200     STRING       * const fq_class    = VTABLE_get_string(interp, cur_class);
201     PMC          * const class_cache = Parrot_pmc_new(interp, enum_class_Hash);
202     VTABLE_set_pmc_keyed_str(interp, cache, fq_class, class_cache);
204     /* Iterate over the attributes. */
205     while (VTABLE_get_bool(interp, iter)) {
206         /* Get attribute. */
207         PMC    * const cur_attrib  = VTABLE_get_pmc_keyed_str(interp,
208                 attribs, VTABLE_shift_string(interp, iter));
210         /* Get attribute name and append it to the key. */
211         STRING * const name_str    = CONST_STRING(interp, "name");
212         STRING * const attrib_name = VTABLE_get_string_keyed_str(
213             interp, cur_attrib, name_str);
215         STRING * const full_key    = Parrot_str_concat(interp, fq_class, attrib_name);
217         /* Insert into hash, along with index. */
218         VTABLE_set_integer_keyed_str(interp, attrib_index, full_key, cur_index);
219         VTABLE_set_integer_keyed_str(interp, class_cache, attrib_name, cur_index);
220         ++cur_index;
221     }
223     return cur_index;
228 =item C<static void build_attrib_index(PARROT_INTERP, PMC *self)>
230 This function builds the attribute index (table to map class name and
231 attribute name to an index) for the current class.
233 =cut
237 static void
238 build_attrib_index(PARROT_INTERP, ARGIN(PMC *self))
240     ASSERT_ARGS(build_attrib_index)
241     Parrot_Class_attributes * const _class      = PARROT_CLASS(self);
242     int                  cur_index    = 0;
243     PMC * const          attrib_index = Parrot_pmc_new(interp, enum_class_Hash);
244     PMC * const          cache        = Parrot_pmc_new(interp, enum_class_Hash);
245     const int            num_classes  = VTABLE_elements(interp, _class->all_parents);
246     int                  i;
248     /* Go over the list of all parents to construct the attribute index. */
249     for (i = 0; i < num_classes; ++i) {
250        /* Get the class and check that it respects the standard class interface
251         * (if not we don't know how it stores its attributes, so we'll have to
252         * delegate the lookup). */
253         PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp,
254                                     _class->all_parents, i);
256         if (PObj_is_class_TEST(cur_class))
257             cur_index = cache_class_attribs(interp, cur_class,
258                 attrib_index, cache, cur_index);
259     }
261     /* Store built attribute index and invalidate cache. */
262     _class->attrib_index = attrib_index;
263     _class->attrib_cache = cache;
268 =item C<static void init_class_from_hash(PARROT_INTERP, PMC *self, PMC *info)>
270 Takes a hash and initializes the class based on it.
272 =cut
276 static void
277 init_class_from_hash(PARROT_INTERP, ARGMOD(PMC *self), ARGIN_NULLOK(PMC *info))
279     ASSERT_ARGS(init_class_from_hash)
280     Parrot_Class_attributes * const _class      = PARROT_CLASS(self);
281     STRING       * const name_str    = CONST_STRING(interp, "name");
282     STRING       * const parents_str = CONST_STRING(interp, "parents");
283     STRING       * const methods_str = CONST_STRING(interp, "methods");
284     STRING       * const roles_str   = CONST_STRING(interp, "roles");
285     STRING       * const attrs_str   = CONST_STRING(interp, "attributes");
286     PMC          *old_ns;
287     STRING       *resolve_method_str;
289     /* Ensure we actually have some initialization info. */
290     if (PMC_IS_NULL(info))
291         return;
293     /* Take a copy of the current namespace the class is attached to. */
294     old_ns = _class->_namespace;
296     /* Check if we have a name/namespace. */
297     if (VTABLE_exists_keyed_str(interp, info, name_str)) {
298         STRING *new_name;
299         PMC    *new_namespace;
300         PMC    *name_arg = VTABLE_get_pmc_keyed_str(interp, info, name_str);
301         VTABLE *new_vtable;
302         INTVAL type_num;
304         /* If we were passed a namespace PMC, set the namespace attribute
305          * directly. Otherwise, lookup or create the appropriate namespace. */
306         if (name_arg->vtable->base_type == enum_class_NameSpace) {
307             new_namespace = name_arg;
308             name_arg      = Parrot_ns_get_name(interp, new_namespace);
309             VTABLE_shift_string(interp, name_arg);
310         }
311         else {
312             PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp,
313                     interp->HLL_namespace, Parrot_pcc_get_HLL(interp, CURRENT_CONTEXT(interp)));
314             new_namespace = Parrot_ns_make_namespace_keyed(interp, hll_ns, name_arg);
315         }
317         if (PMC_IS_NULL(new_namespace))
318             Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
319                 "Failed to set namespace for class.");
321         /* Set the name of the class to the name of the innermost namespace
322          * associated with the class.  */
323         new_name = VTABLE_get_string(interp, new_namespace);
325         if (STRING_IS_NULL(new_name) || STRING_IS_EMPTY(new_name))
326               Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
327                 "Failed to set name for class.");
329         _class->_namespace = new_namespace;
330         _class->name       = new_name;
332         /* At this point we know the class isn't anonymous */
333         CLASS_is_anon_CLEAR(self);
335         /* Register a type number for the class. */
336         type_num   = Parrot_oo_register_type(interp, name_arg, new_namespace);
338         /* Link the type number with the class's vtable. */
339         new_vtable = Parrot_clone_vtable(interp, self->vtable);
341         new_vtable->base_type         = type_num;
342         new_vtable->pmc_class         = self;
343         new_vtable->whoami            = VTABLE_get_string(interp, self);
344         new_vtable->mro               = _class->all_parents;
345         new_vtable->ro_variant_vtable =
346                 Parrot_clone_vtable(interp, self->vtable->ro_variant_vtable);
348         /* Store the class's vtable in the global table */
349         interp->vtables[type_num]     = new_vtable;
351         _class->id                    = type_num;
352     }
354     /* If we were attached to a namespace and are now attached to a new one,
355      * need to unset ourselves in the old namespace. */
356     if (!PMC_IS_NULL(old_ns) && _class->_namespace != old_ns)
357         Parrot_pcc_invoke_method_from_c_args(interp, old_ns, CONST_STRING(interp, "set_class"), "P->", PMCNULL);
359     /* Link namespace to this class, if there is one. */
360     if (!PMC_IS_NULL(_class->_namespace)) {
361         STRING * const set_class_str = CONST_STRING(interp, "set_class");
362         Parrot_pcc_invoke_method_from_c_args(interp, _class->_namespace, set_class_str, "P->",
363                                              self);
364     }
366     /* Initialize resolve_method. */
367     resolve_method_str = CONST_STRING(interp, "resolve_method");
368     if (VTABLE_exists_keyed_str(interp, info, resolve_method_str)) {
369         /* Set it. */
370         _class->resolve_method =
371             VTABLE_get_pmc_keyed_str(interp, info, resolve_method_str);
372     }
374     /* Initialize parents, if we have any. */
375     if (VTABLE_exists_keyed_str(interp, info, parents_str)) {
376         /* Loop over parents array and add them. */
377         PMC * const parent_list = VTABLE_get_pmc_keyed_str(interp, info,
378             parents_str);
379         const int  parent_count = VTABLE_elements(interp, parent_list);
380         int i;
382         for (i = 0; i < parent_count; ++i)
383             VTABLE_add_parent(interp, self,
384                 VTABLE_get_pmc_keyed_int(interp, parent_list, i));
385     }
387     /* Initialize roles, if we have any. */
388     if (VTABLE_exists_keyed_str(interp, info, roles_str)) {
389         /* Loop over roles array and compose them. */
390         PMC * const role_list  = VTABLE_get_pmc_keyed_str(interp, info,
391             roles_str);
392         const int   role_count = VTABLE_elements(interp, role_list);
393         int i;
395         for (i = 0; i < role_count; ++i)
396             VTABLE_add_role(interp, self,
397                 VTABLE_get_pmc_keyed_int(interp, role_list, i));
398     }
400     /* Initialize attributes, if we have any. */
401     if (VTABLE_exists_keyed_str(interp, info, attrs_str)) {
402         /* Loop over attributes array and add them. */
403         PMC * const attrs_name_list = VTABLE_get_pmc_keyed_str(interp, info,
404                                             attrs_str);
405         const int   attrib_count    = VTABLE_elements(interp, attrs_name_list);
406         int i;
408         for (i = 0; i < attrib_count; ++i) {
409             STRING * const attr_name = VTABLE_get_string_keyed_int(interp,
410                 attrs_name_list, i);
411             VTABLE_add_attribute(interp, self, attr_name, PMCNULL);
412         }
413     }
415     /* Initialize methods. */
416     if (VTABLE_exists_keyed_str(interp, info, methods_str)) {
417         /* Get the methods hash. */
418         PMC * const methods = VTABLE_get_pmc_keyed_str(interp, info,
419             methods_str);
421         /* Iterate over the list of methods. */
422         PMC * const iter    = VTABLE_get_iter(interp, methods);
424         while (VTABLE_get_bool(interp, iter)) {
425             /* Add the method. */
426             STRING * const method_name = VTABLE_shift_string(interp, iter);
427             PMC    * const method_pmc  = VTABLE_get_pmc_keyed_str(interp,
428                 methods, method_name);
429             VTABLE_add_method(interp, self, method_name, method_pmc);
430         }
431     }
433     /* Extract any methods from the namespace */
434     Parrot_oo_extract_methods_from_namespace(interp, self, _class->_namespace);
439 =item C<static void initialize_parents(PARROT_INTERP, PMC *object, PMC
440 *all_parents)>
442 =cut
446 static void
447 initialize_parents(PARROT_INTERP, ARGIN(PMC *object), ARGIN(PMC *all_parents))
449     ASSERT_ARGS(initialize_parents)
450     INTVAL  parent_index = VTABLE_elements(interp, all_parents) - 1;
451     STRING * const name  = CONST_STRING(interp, "init");
453     /* Loop through the parents in reverse MRO order. */
454     for (; parent_index >= 0; --parent_index) {
455         PMC *meth;
456         PMC * const parent = VTABLE_get_pmc_keyed_int(interp,
457             all_parents, parent_index);
459         /* PMCProxy parents store an instance to delegate to */
460         if (parent->vtable->base_type == enum_class_PMCProxy) {
461             PMC    *proxy     = VTABLE_instantiate(interp, parent, PMCNULL);
462             STRING *proxy_str = CONST_STRING(interp, "proxy");
463             VTABLE_set_attr_keyed(interp, object, parent, proxy_str, proxy);
464         }
466         meth = Parrot_oo_find_vtable_override_for_class(interp, parent, name);
468         if (!PMC_IS_NULL(meth)) {
469             /* preserve current_object */
470             Parrot_ext_call(interp, meth, "Pi->", object);
471         }
472     }
477 =item C<static void initialize_parents_pmc(PARROT_INTERP, PMC *object, PMC
478 *all_parents, PMC *init)>
480 =cut
484 static void
485 initialize_parents_pmc(PARROT_INTERP, ARGIN(PMC *object),
486         ARGIN(PMC *all_parents), ARGIN(PMC *init))
488     ASSERT_ARGS(initialize_parents_pmc)
489     INTVAL parent_index = VTABLE_elements(interp, all_parents) - 1;
490     STRING * const name  = CONST_STRING(interp, "init_pmc");
492     /* Loop through the parents in reverse MRO order. */
493     for (; parent_index >= 0; --parent_index) {
494         PMC *meth;
495         PMC * const parent = VTABLE_get_pmc_keyed_int(interp,
496             all_parents, parent_index);
498         /* PMCProxy parents store an instance to delegate to */
499         if (parent->vtable->base_type == enum_class_PMCProxy) {
500             PMC    * const proxy     = VTABLE_instantiate(interp, parent, init);
501             STRING * const proxy_str = CONST_STRING(interp, "proxy");
502             VTABLE_set_attr_keyed(interp, object, parent, proxy_str, proxy);
503         }
505         meth = Parrot_oo_find_vtable_override_for_class(interp, parent, name);
507         if (!PMC_IS_NULL(meth))
508             Parrot_ext_call(interp, meth, "PiP->", object, init);
509     }
514 =item C<static STRING * make_class_name(PARROT_INTERP, PMC *SELF)>
516 This function makes and caches the name of this class, returning the string
517 directly.  C<VTABLE_isa()> uses the name without copying it, for efficiency
518 reasons, as it does not modify the STRING.  C<VTABLE_get_string()> makes a
519 copy of the STRING, so its callers are free to modify it.
521 =cut
525 PARROT_CANNOT_RETURN_NULL
526 static STRING *
527 make_class_name(PARROT_INTERP, ARGIN(PMC *SELF))
529     ASSERT_ARGS(make_class_name)
530     Parrot_Class_attributes * const  _class    = PARROT_CLASS(SELF);
531     PMC          * const _namespace = _class->_namespace;
533     if (!PMC_IS_NULL(_namespace)) {
534         if (_class->fullname)
535             return _class->fullname;
536         else {
538             /* Call the 'get_name' method on the class's associated
539              * namespace to retrieve a fully qualified list of names, then
540              * join the list with a semicolon.  */
541             PMC * const names = Parrot_ns_get_name(interp, _namespace);
543             if (!PMC_IS_NULL(names))
544                 /* remove the HLL namespace name */
545                 VTABLE_shift_string(interp, names);
546                 _class->fullname = Parrot_str_join(interp, CONST_STRING(interp, ";"), names);
547                 return _class->fullname;
548         }
549     }
551     /* Otherwise, copy the stored string name of the class. */
552     return _class->name;
557 =item C<static void calculate_mro(PARROT_INTERP, PMC *SELF, INTVAL num_parents)>
559 Calculates the C3 method resolution order for this class. C3 is the name of an
560 algorithm used to calculate the method resolution order (MRO) to use in a
561 system with multiple inheritance. For more information see the documentation
562 associated with C<Parrot_ComputeMRO_C3>.
564 =cut
568 static void
569 calculate_mro(PARROT_INTERP, ARGIN(PMC *SELF), INTVAL num_parents)
571     ASSERT_ARGS(calculate_mro)
572     Parrot_Class_attributes  * const _class = PARROT_CLASS(SELF);
574     /* SELF is already on the all_parents */
575     if (num_parents == 0)
576         return;
578     if (num_parents == 1) {
579         STRING * const ap         = CONST_STRING(interp, "all_parents");
580         PMC    * const parent     = VTABLE_get_pmc_keyed_int(interp,
581                 _class->parents, 0);
582         PMC    * const parent_mro = VTABLE_inspect_str(interp, parent, ap);
583         PMC    * const mro        = VTABLE_clone(interp, parent_mro);
584         VTABLE_unshift_pmc(interp, mro, SELF);
585         _class->all_parents = mro;
586     }
587     else
588         _class->all_parents = Parrot_ComputeMRO_C3(interp, SELF);
590     if (!CLASS_is_anon_TEST(SELF))
591         interp->vtables[VTABLE_type(interp, SELF)]->mro = _class->all_parents;
596 =back
598 =head2 Functions
600 =over 4
602 =cut
606 pmclass Class auto_attrs {
608     ATTR INTVAL id;             /* The type number of the PMC. */
609     ATTR STRING *name;          /* The name of the class. */
610     ATTR STRING *fullname;      /* The name of the class. */
611     ATTR PMC *_namespace;       /* The namespace it's linked to, if any. */
612     ATTR INTVAL instantiated;   /* Any instantiations since last modification? */
613     ATTR PMC *parents;          /* Immediate parent classes. */
614     ATTR PMC *all_parents;      /* Cached list of ourself and all parents, in MRO order. */
615     ATTR PMC *roles;            /* An array of roles. */
616     ATTR PMC *methods;          /* Hash of method names to methods in this class. */
617     ATTR PMC *vtable_overrides; /* Hash of Parrot v-table methods we override. */
618     ATTR PMC *attrib_metadata;  /* Hash of attributes in this class to hashes of metadata. */
619     ATTR PMC *attrib_index;     /* Lookup table for attributes in this and parents. */
620     ATTR PMC *attrib_cache;     /* Cache of visible attrib names to indexes. */
621     ATTR PMC *resolve_method;   /* List of method names the class provides to resolve
622                                  * conflicts with methods from roles. */
623     ATTR PMC  *parent_overrides;
624     ATTR PMC  *meth_cache;
625     ATTR Hash *isa_cache;
629 =item C<void init()>
631 Initializes a Class PMC.
633 =item C<void init_pmc(PMC *init_data)>
635 The actual class creation code, called from C<newclass> opcode. The C<init_data>
636 argument may be either the name of the class or a hash of initialization
637 arguments. The class is attached to the current HLL namespace.
639 =cut
643     VTABLE void init() {
644         Parrot_Class_attributes * const _class =
645                 (Parrot_Class_attributes *) PMC_data(SELF);
647         /* Set flag for custom GC mark. */
648         PObj_custom_mark_destroy_SETALL(SELF);
650         /* Set up the object. */
651         _class->name            = CONST_STRING(INTERP, "");
652         _class->_namespace      = PMCNULL;
653         _class->parents         = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
654         _class->all_parents     = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
655         _class->roles           = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
656         _class->methods         = Parrot_pmc_new(INTERP, enum_class_Hash);
657         _class->attrib_metadata = Parrot_pmc_new(INTERP, enum_class_Hash);
658         _class->attrib_index    = PMCNULL;
659         _class->attrib_cache    = PMCNULL;
660         _class->meth_cache      = PMCNULL;
661         _class->resolve_method  = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
663         _class->vtable_overrides = Parrot_pmc_new(INTERP, enum_class_Hash);
664         _class->parent_overrides = Parrot_pmc_new(INTERP, enum_class_Hash);
666         _class->isa_cache        = parrot_create_hash(INTERP,
667             enum_type_INTVAL, Hash_key_type_PMC_ptr);
670         /* We put ourself on the all parents list. */
671         VTABLE_push_pmc(INTERP, _class->all_parents, SELF);
673         /* We are a class. */
674         PObj_is_class_SET(SELF);
676         /* By default we're anonymous. */
677         CLASS_is_anon_SET(SELF);
678     }
680     VTABLE void init_pmc(PMC *init_data) {
681         PMC           *arg;
682         const INTVAL   arg_type = VTABLE_type(INTERP, init_data);
683         STRING * const name_str = CONST_STRING(INTERP, "name");
685         /* Set up the object. */
686         SELF.init();
688         /* fast attempt to determine init_data type */
689         switch (arg_type) {
690           case enum_class_String:
691           case enum_class_Key:
692           case enum_class_ResizableStringArray:
693           case enum_class_NameSpace:
694             /* set only the name property */
695             arg = Parrot_pmc_new(INTERP, enum_class_Hash);
696             VTABLE_set_pmc_keyed_str(INTERP, arg, name_str, init_data);
697             break;
699           case enum_class_Hash:
700             arg = init_data;
701             break;
703             /* slow attempt to determine init_data type */
704           default:
705             if (VTABLE_isa(INTERP, init_data, CONST_STRING(INTERP, "String"))
706              || VTABLE_isa(INTERP, init_data, CONST_STRING(INTERP, "Key"))
707              || VTABLE_isa(INTERP, init_data, CONST_STRING(INTERP, "ResizableStringArray"))) {
708                 /* set only the name property */
709                 arg = Parrot_pmc_new(INTERP, enum_class_Hash);
710                 VTABLE_set_pmc_keyed_str(INTERP, arg, name_str, init_data);
711             }
713             if (VTABLE_isa(INTERP, init_data, CONST_STRING(INTERP, "Hash")))
714                 arg = init_data;
715             else
716                 Parrot_ex_throw_from_c_args(INTERP, NULL,
717                         EXCEPTION_INVALID_OPERATION,
718                             "Invalid class name key in init_pmc for Class");
719             break;
720         }
722         /* Initialize the class with the supplied data. */
723         init_class_from_hash(INTERP, SELF, arg);
724     }
726     void destroy() {
727         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
728         parrot_hash_destroy(INTERP, _class->isa_cache);
729     }
733 =item C<STRING *get_string()>
735 Returns the name of the class (without the HLL namespace).
737 =cut
741     VTABLE STRING *get_string() {
742         return make_class_name(INTERP, SELF);
743     }
747 =item C<void mark()>
749 Marks any referenced strings and PMCs in the structure as live.
751 =cut
755     VTABLE void mark() {
756         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
757         Parrot_gc_mark_STRING_alive(INTERP, _class->name);
758         Parrot_gc_mark_STRING_alive(INTERP, _class->fullname);
759         Parrot_gc_mark_PMC_alive(INTERP, _class->_namespace);
760         Parrot_gc_mark_PMC_alive(INTERP, _class->parents);
761         Parrot_gc_mark_PMC_alive(INTERP, _class->all_parents);
762         Parrot_gc_mark_PMC_alive(INTERP, _class->roles);
763         Parrot_gc_mark_PMC_alive(INTERP, _class->methods);
764         Parrot_gc_mark_PMC_alive(INTERP, _class->vtable_overrides);
765         Parrot_gc_mark_PMC_alive(INTERP, _class->parent_overrides);
766         Parrot_gc_mark_PMC_alive(INTERP, _class->attrib_metadata);
767         Parrot_gc_mark_PMC_alive(INTERP, _class->attrib_index);
768         Parrot_gc_mark_PMC_alive(INTERP, _class->attrib_cache);
769         Parrot_gc_mark_PMC_alive(INTERP, _class->resolve_method);
770         Parrot_gc_mark_PMC_alive(INTERP, _class->meth_cache);
771         if (_class->isa_cache)
772             parrot_mark_hash(INTERP, _class->isa_cache);
773     }
778 =item C<void add_attribute(STRING *name, PMC *type)>
780 Adds the given attribute (C<name>) with an optional C<type>.
781 Throws an exception if the current class has been instantiated.
782 Enters the attribute in the C<attrib_metadata> table.
783 Returns an error if an attribute of C<name> already exists.
785 =cut
789     VTABLE void add_attribute(STRING *name, PMC *type) {
790         Parrot_Class_attributes * const _class        = PARROT_CLASS(SELF);
791         PMC          * const new_attribute = Parrot_pmc_new(INTERP, enum_class_Hash);
793         /* If we've been instantiated already, not allowed. */
794         if (_class->instantiated)
795             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
796                 "Modifications to classes are not allowed after instantiation.");
798         /* If we already have an attribute of this name, it's an error. */
799         if (VTABLE_exists_keyed_str(INTERP, _class->attrib_metadata, name))
800             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
801                 "Attribute '%Ss' already exists in '%Ss'.", name,
802                 VTABLE_get_string(INTERP, SELF));
804         /* Set name and type. */
805         VTABLE_set_string_keyed_str(INTERP, new_attribute, CONST_STRING(INTERP, "name"), name);
807         if (!PMC_IS_NULL(type))
808             VTABLE_set_pmc_keyed_str(INTERP, new_attribute, CONST_STRING(INTERP, "type"), type);
810         /* Enter the attribute in the attrib_metadata table. */
811         VTABLE_set_pmc_keyed_str(INTERP, _class->attrib_metadata, name,
812             new_attribute);
813     }
817 =item C<void remove_attribute(STRING *name)>
819 Removes the given attribute (C<name>) from the class. Throws an exception if
820 the current class has been instantiated, or if the class has no attribute
821 C<name>.
823 =cut
827     VTABLE void remove_attribute(STRING *name) {
828         Parrot_Class_attributes * const _class        = PARROT_CLASS(SELF);
830         /* If we've been instantiated already, not allowed. */
831         if (_class->instantiated)
832             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
833                 "Modifications to classes are not allowed after instantiation.");
835         /* If we don't have an attribute of this name, it's an error. */
836         if (!VTABLE_exists_keyed_str(INTERP, _class->attrib_metadata, name))
837             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
838                 "Attribute '%Ss' cannot be removed, does not exist in '%Ss'.", name,
839                 VTABLE_get_string(INTERP, SELF));
841         /* Remove the attribute from the attrib_metadata table. */
842         VTABLE_delete_keyed_str(INTERP, _class->attrib_metadata, name);
844         build_attrib_index(INTERP, SELF);
845     }
849 =item C<void add_method(STRING *name, PMC *sub)>
851 Adds the given sub PMC as a method with the given name.
853 =cut
856     VTABLE void add_method(STRING *name, PMC *sub) {
857         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
858         PMC                     * const method =
859                  VTABLE_get_pmc_keyed_str(INTERP, _class->methods, name);
861         /* If we have already added a method with this name... */
862         if (!PMC_IS_NULL(method)) {
863             if (method == sub)
864                 return;
866             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
867                 "A method named '%S' already exists in class '%S'. "
868                 "It may have been supplied by a role.",
869                 name, VTABLE_get_string(INTERP, SELF));
870         }
872         /* Enter it into the table. */
873         VTABLE_set_pmc_keyed_str(INTERP, _class->methods, name, sub);
874     }
878 =item C<void remove_method(STRING *name)>
880 Removes the method with the given name.
882 =cut
885     VTABLE void remove_method(STRING *name) {
886         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
887         if (VTABLE_exists_keyed_str(INTERP, _class->methods, name))
888             VTABLE_delete_keyed_str(INTERP, _class->methods, name);
889         else
890             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
891                 "No method named '%S' to remove in class '%S'.",
892                 name, VTABLE_get_string(INTERP, SELF));
893     }
897 =item C<void add_vtable_override(STRING *name, PMC *sub)>
899 Adds the given sub PMC as a vtable override with the given name.
901 =cut
904     VTABLE void add_vtable_override(STRING *name, PMC *sub) {
905         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
906         PMC                     * const vtable =
907             VTABLE_get_pmc_keyed_str(INTERP, _class->vtable_overrides, name);
909         /* If we have already added a vtable override with this name... */
910         if (!PMC_IS_NULL(vtable)) {
911             if (vtable == sub)
912                 return;
914             Parrot_ex_throw_from_c_args(INTERP, NULL,
915                 EXCEPTION_INVALID_OPERATION,
916                 "A vtable override named '%S' already exists in class '%S'. "
917                 "It may have been supplied by a role.",
918                 name, VTABLE_get_string(INTERP, SELF));
919         }
921         /* Check that the name is actually valid as a vtable override */
922         if (Parrot_get_vtable_index(INTERP, name) == -1)
923             Parrot_ex_throw_from_c_args(INTERP, NULL,
924                 EXCEPTION_METHOD_NOT_FOUND,
925                 "'%S' is not a valid vtable function name.", name);
927         /* Add it to vtable list. */
928         VTABLE_set_pmc_keyed_str(INTERP, _class->vtable_overrides, name, sub);
929     }
933 =item C<void add_parent(PMC *parent)>
935 Adds the supplied PMC to the list of parents for the class.
937 =cut
940     VTABLE void add_parent(PMC *parent) {
941         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
942         int parent_count, index;
944         /* If we've been instantiated already, not allowed. */
945         if (_class->instantiated)
946             Parrot_ex_throw_from_c_args(INTERP, NULL,
947                 EXCEPTION_INVALID_OPERATION,
948                 "Modifications to classes are not allowed after instantiation.");
950         /* Ensure it really is a class. */
951         if (!PObj_is_class_TEST(parent))
952             Parrot_ex_throw_from_c_args(INTERP, NULL,
953                 EXCEPTION_INVALID_OPERATION, "Parent isn't a Class.");
955         /* Check is not self */
956         if (parent == SELF)
957             Parrot_ex_throw_from_c_args(INTERP, NULL,
958                     EXCEPTION_INVALID_OPERATION, "Can't be own parent");
960         /* get number of direct parents */
961         parent_count = VTABLE_elements(INTERP, _class->parents);
963         /* iterate over all direct parents, check whether this class already has
964          * the parent being added. */
965         for (index = 0; index < parent_count; ++index) {
966             /* get the next parent */
967             PMC * const current_parent = VTABLE_get_pmc_keyed_int(INTERP,
968                                       _class->parents, index);
970             /* throw an exception if we already have this parent */
971             if (current_parent == parent)
972                 Parrot_ex_throw_from_c_args(INTERP, NULL,
973                     EXCEPTION_INVALID_OPERATION,
974                     "The class '%S' already has a parent class '%S'. "
975                     "It may have been supplied by a role.",
976                     VTABLE_get_string(INTERP, SELF),
977                     VTABLE_get_string(INTERP, parent));
978         }
980         /* Check that none of the parents is self */
981         parent_count = VTABLE_elements(INTERP, PARROT_CLASS(parent)->all_parents);
983         for (index = 0; index < parent_count; ++index) {
984             /* get the next parent */
985             PMC * const current_parent = VTABLE_get_pmc_keyed_int(INTERP,
986                 PARROT_CLASS(parent)->all_parents, index);
988             if (current_parent == SELF)
989                 Parrot_ex_throw_from_c_args(INTERP, NULL,
990                     EXCEPTION_INVALID_OPERATION,
991                     "Loop in class hierarchy: '%S' is an ancestor of '%S'.",
992                     VTABLE_get_string(INTERP, SELF),
993                     VTABLE_get_string(INTERP, parent));
994         }
996         /* Add to the lists of our immediate parents and all parents. */
997         VTABLE_push_pmc(INTERP, _class->parents, parent);
998         parrot_hash_put(INTERP, _class->isa_cache, (void *)parent, (void *)1);
999         calculate_mro(INTERP, SELF, parent_count + 1);
1000     }
1004 =item C<void remove_parent(PMC *parent)>
1006 Remove the supplied class object from the list of parents for the class.
1007 Throws an exception if parent is null, is not a class, or is not a parent, or
1008 if the class has been instantiated.
1010 =cut
1013     VTABLE void remove_parent(PMC *parent) {
1014         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
1015         int parent_count, index;
1017         /* If we've been instantiated already, not allowed. */
1018         if (_class->instantiated)
1019             Parrot_ex_throw_from_c_args(INTERP, NULL,
1020                 EXCEPTION_INVALID_OPERATION,
1021                 "Modifications to classes are not allowed after instantiation.");
1023         /* Ensure it really is a class. */
1024         if (!PObj_is_class_TEST(parent))
1025             Parrot_ex_throw_from_c_args(INTERP, NULL,
1026                 EXCEPTION_INVALID_OPERATION, "Parent isn't a Class.");
1028         /* get number of direct parents */
1029         parent_count = VTABLE_elements(INTERP, _class->parents);
1031         /* iterate over all direct parents, looking for the parent to remove */
1032         for (index = 0; index < parent_count; ++index) {
1033             /* get the next parent */
1034             PMC * const current_parent = VTABLE_get_pmc_keyed_int(INTERP,
1035                                       _class->parents, index);
1036             if (current_parent == parent)
1037                 break;
1038         }
1040         if (index >= parent_count)
1041             Parrot_ex_throw_from_c_args(INTERP, NULL,
1042                 EXCEPTION_INVALID_OPERATION,
1043                 "Can't remove_parent: is not a parent.");
1045         VTABLE_delete_keyed_int(INTERP, _class->parents, index);
1046         parrot_hash_put(INTERP, _class->isa_cache, (void *)parent, (void *)0);
1047         calculate_mro(INTERP, SELF, parent_count - 1);
1048     }
1052 =item C<void add_role(PMC *role)>
1054 Adds the supplied PMC to the list of roles for the class, provided there are
1055 no conflicts.
1057 =cut
1060     VTABLE void add_role(PMC *role) {
1061         const Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
1063         /* Do the composition. */
1064         Parrot_ComposeRole(INTERP, role,
1065             _class->resolve_method, !PMC_IS_NULL(_class->resolve_method),
1066            PMCNULL, 0, _class->methods, _class->roles);
1067     }
1071 =item C<PMC *inspect_str(STRING *what)>
1073 Provides introspection of a specific piece of information about the class. The
1074 available information is:
1076 =over 8
1078 =item name
1080 String PMC containing the name of the class
1082 =item namespace
1084 NameSpace PMC of the the namespace attached to the class.
1086 =item attributes
1088 Hash keyed on attribute name, where the value is a hash describing it.
1090 =item methods
1092 Hash keyed on method name, value is an invokable PMC. Includes methods composed
1093 in from roles.
1095 =item roles
1097 Array of Role PMCs. Includes roles done by the roles that were composed into
1098 this class.
1100 =item parents
1102 Array of Class PMCs representing the direct parents of this class.
1104 =back
1106 =cut
1109     VTABLE PMC *inspect_str(STRING *what) {
1110         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
1112         /* What should we return? */
1113         PMC *found;
1115         if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "attributes"))) {
1116             found = _class->attrib_metadata;
1117         }
1118         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "parents"))) {
1119             found = _class->parents;
1120         }
1121         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "name"))) {
1122             found = Parrot_pmc_new(INTERP, enum_class_String);
1123             VTABLE_set_string_native(INTERP, found, _class->name);
1124         }
1125         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "id"))) {
1126             found = Parrot_pmc_new_init_int(INTERP, enum_class_Integer, _class->id);
1127         }
1128         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "namespace"))) {
1129             /* Should not clone this. */
1130             return _class->_namespace;
1131         }
1132         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "attrib_index"))) {
1133             found = _class->attrib_index;
1134         }
1135         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "methods"))) {
1136             found = _class->methods;
1137         }
1138         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "vtable_overrides"))) {
1139             found = _class->vtable_overrides;
1140         }
1141         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "all_parents"))) {
1142             found = _class->all_parents;
1143         }
1144         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "roles"))) {
1145             found = _class->roles;
1146         }
1147         else if (Parrot_str_equal(INTERP, what, CONST_STRING(INTERP, "flags"))) {
1148             found = Parrot_pmc_new_init_int(INTERP, enum_class_Integer,
1149                 (INTVAL)PObj_get_FLAGS(SELF));
1150         }
1151         else
1152             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
1153                 "Unknown introspection value '%S'", what);
1155         /* return found value */
1156         if (PMC_IS_NULL(found))
1157             return PMCNULL;
1159         if (found->vtable->base_type == enum_class_Hash) {
1160             /* for Hash return values, create and return a shallow
1161              * clone because the VTABLE_clone does a deep clone */
1162             PMC * const hash = Parrot_pmc_new(INTERP, enum_class_Hash);
1163             Hash       *src  = (Hash *)VTABLE_get_pointer(interp, found);
1164             Hash       *dest = (Hash *)VTABLE_get_pointer(interp, hash);
1166             parrot_hash_clone_prunable(interp, src, dest, 0);
1168             return hash;
1169         }
1171         return VTABLE_clone(INTERP, found);
1172     }
1176 =item C<PMC *inspect()>
1178 Returns a Hash describing the class, with key/value pairs as described in
1179 inspect_str.
1181 =cut
1184     VTABLE PMC *inspect() {
1185         /* Create a hash, then use inspect_str to get all of the data to
1186          * fill it up with. */
1187         PMC    * const metadata    = Parrot_pmc_new(INTERP, enum_class_Hash);
1188         STRING * const name_str    = CONST_STRING(INTERP, "name");
1189         STRING * const ns_str      = CONST_STRING(INTERP, "namespace");
1190         STRING * const attrs_str   = CONST_STRING(INTERP, "attributes");
1191         STRING * const meths_str   = CONST_STRING(INTERP, "methods");
1192         STRING * const parents_str = CONST_STRING(INTERP, "parents");
1193         STRING * const roles_str   = CONST_STRING(INTERP, "roles");
1194         STRING * const flags_str   = CONST_STRING(INTERP, "flags");
1196         VTABLE_set_pmc_keyed_str(INTERP, metadata, name_str,
1197             VTABLE_inspect_str(INTERP, SELF, name_str));
1199         VTABLE_set_pmc_keyed_str(INTERP, metadata, ns_str,
1200             VTABLE_inspect_str(INTERP, SELF, ns_str));
1202         VTABLE_set_pmc_keyed_str(INTERP, metadata, attrs_str,
1203             VTABLE_inspect_str(INTERP, SELF, attrs_str));
1205         VTABLE_set_pmc_keyed_str(INTERP, metadata, meths_str,
1206             VTABLE_inspect_str(INTERP, SELF, meths_str));
1208         VTABLE_set_pmc_keyed_str(INTERP, metadata, parents_str,
1209             VTABLE_inspect_str(INTERP, SELF, parents_str));
1211         VTABLE_set_pmc_keyed_str(INTERP, metadata, roles_str,
1212             VTABLE_inspect_str(INTERP, SELF, roles_str));
1214         VTABLE_set_pmc_keyed_str(INTERP, metadata, flags_str,
1215             VTABLE_inspect_str(INTERP, SELF, flags_str));
1217         return metadata;
1218     }
1222 =item C<PMC *clone()>
1224 Returns an anonymous copy of the class (with no name and no link to a
1225 namespace). Unsets the instantiated flag, allowing modifications.
1227 =cut
1231     VTABLE PMC *clone() {
1232         Parrot_Class_attributes * const _class    = PARROT_CLASS(SELF);
1234         /* Create the new class PMC, of the same type of this one (we may
1235          * have been subclassed). */
1236         PMC          * const copy      = SUPER();
1238         /* Clone parents, roles, methods, attributes and resolve data. We do
1239          * not copy name/namespace related stuff (need anonymous clone) or
1240          * stuff that gets computed on the first instantiation. */
1242         Parrot_Class_attributes * const new_class = PARROT_CLASS(copy);
1244         new_class->name                = CONST_STRING(INTERP, "");
1245         new_class->_namespace          = PMCNULL;
1246         new_class->parents             = VTABLE_clone(INTERP, _class->parents);
1247         new_class->roles               = VTABLE_clone(INTERP, _class->roles);
1248         new_class->methods             = VTABLE_clone(INTERP, _class->methods);
1249         new_class->vtable_overrides    = VTABLE_clone(INTERP,
1250                                             _class->vtable_overrides);
1251         new_class->parent_overrides    = VTABLE_clone(INTERP,
1252                                             _class->parent_overrides);
1253         new_class->attrib_metadata     = VTABLE_clone(INTERP,
1254                                             _class->attrib_metadata);
1255         new_class->resolve_method      = VTABLE_clone(INTERP,
1256                                             _class->resolve_method);
1258         /* Return cloned class. */
1259         return copy;
1260     }
1264 =item C<PMC *clone_pmc(PMC *args)>
1266 Makes a copy of the class, then modifies or adds to it based upon the contents
1267 of the supplied initialization data. If a new name or namespace is not supplied
1268 in C<args> then the cloned class will be anonymous. The instantiated flag is
1269 unset to allow further modifications.
1271 =cut
1275     VTABLE PMC *clone_pmc(PMC *args) {
1276         /* Do the standard clone. */
1277         PMC * const copy = SELF.clone();
1279         init_class_from_hash(INTERP, copy, args);
1281         return copy;
1282     }
1286 =item C<PMC *instantiate(PMC *init)>
1288 Creates a new PMC object of the type of the class and calls init().
1290 =cut
1294     VTABLE PMC *instantiate(PMC *init) {
1295         Parrot_Class_attributes  * const _class = PARROT_CLASS(SELF);
1296         PMC                      *object;
1298         /* If we've not been instantiated before... */
1299         if (!_class->instantiated) {
1300             /* Check that we have all methods listed in resolve list. */
1301             const int resolve_count  = VTABLE_elements(INTERP,
1302                                                        _class->resolve_method);
1303             const INTVAL cur_hll     = Parrot_pcc_get_HLL(INTERP, CURRENT_CONTEXT(INTERP));
1304             const INTVAL num_parents = VTABLE_elements(INTERP, _class->parents);
1305             INTVAL       mro_length;
1306             int          i;
1308             /* don't use HLL mappings for internal-only data */
1309             Parrot_pcc_set_HLL(INTERP, CURRENT_CONTEXT(INTERP), 0);
1311             for (i = 0; i < resolve_count; ++i) {
1312                 STRING * const check_meth =
1313                     VTABLE_get_string_keyed_int(INTERP, _class->resolve_method, i);
1314                 if (!VTABLE_exists_keyed_str(INTERP, _class->methods, check_meth))
1315                     Parrot_ex_throw_from_c_args(INTERP, NULL,
1316                         EXCEPTION_METHOD_NOT_FOUND, "The method '%S' was named "
1317                         "in the resolve list, but not supplied", check_meth);
1318             }
1320             /* Build full parents list.
1321              * TT #1256:  Need pluggable MRO, for now always do C3. */
1322             calculate_mro(INTERP, SELF, num_parents);
1323             build_attrib_index(INTERP, SELF);
1325             if (PMC_IS_NULL(_class->attrib_index))
1326                 return PMCNULL;
1328             /* See if we have any parents from other universes and if so set a
1329              * flag stating so. */
1330             mro_length = VTABLE_elements(INTERP, _class->all_parents);
1332             for (i = 0; i < mro_length; ++i) {
1333                 PMC * const class_check = VTABLE_get_pmc_keyed_int(INTERP,
1334                     _class->all_parents, i);
1335                 if (class_check->vtable->base_type != enum_class_Class) {
1336                     /* Found one; that's enough. */
1337                     CLASS_has_alien_parents_SET(SELF);
1338                     break;
1339                 }
1340             }
1342             Parrot_pcc_set_HLL(INTERP, CURRENT_CONTEXT(INTERP), cur_hll);
1343         }
1345         /* Set instantiated flag. */
1346         _class->instantiated = 1;
1348         /* Create object. */
1349         object = Parrot_pmc_new_noinit(INTERP, enum_class_Object);
1351         /* Set custom GC mark and destroy on the object. */
1352         PObj_custom_mark_destroy_SETALL(object);
1354         /* Flag that it is an object */
1355         PObj_is_object_SET(object);
1357         /* Initialize the object's underlying structure, pointing it to this
1358          * class. */
1359         /* TODO: this has been changed in order to use auto_attrs in the
1360          * Object PMC. Needs to be redone in a cleaner way. */
1361         {
1362             Parrot_Object_attributes * const objattr =
1363                 PMC_data_typed(object, Parrot_Object_attributes *);
1364             objattr->_class       = SELF;
1365             objattr->attrib_store = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
1366         }
1368         if (!PMC_IS_NULL(init)) {
1369             /* Initialize attributes with the supplied values. */
1370             PMC * const iter = VTABLE_get_iter(INTERP, init);
1372             while (VTABLE_get_bool(INTERP, iter)) {
1373                 STRING * const name  = VTABLE_shift_string(INTERP, iter);
1374                 PMC    * const value = VTABLE_get_pmc_keyed_str(INTERP, init,
1375                                             name);
1377                 /* Set the attribute. */
1378                 VTABLE_set_attr_str(INTERP, object, name, value);
1379             }
1381             /* Check for overrides on the init_pmc vtable function */
1382             initialize_parents_pmc(INTERP, object, _class->all_parents, init);
1383         }
1384         else
1385             /* Check for overrides on the init vtable function */
1386             initialize_parents(INTERP, object, _class->all_parents);
1388         return object;
1389     }
1394 =item C<INTVAL isa_pmc(PMC *class)>
1396 Returns whether the class is or inherits from C<*class>.
1398 =cut
1402     VTABLE INTVAL isa_pmc(PMC *lookup) {
1403         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
1404         PMC          *classobj;
1405         HashBucket   *b;
1406         INTVAL        i, num_classes, retval = 0;
1408         if (PMC_IS_NULL(lookup))
1409             return 0;
1411         if (PObj_is_class_TEST(lookup)) {
1412             if (lookup == SELF)
1413                 return 1;
1414             else
1415                 classobj = lookup;
1416         }
1417         else
1418             classobj = Parrot_oo_get_class(INTERP, lookup);
1420         if (PMC_IS_NULL(classobj))
1421             return 0;
1423         /* Check if the class object is the same as self's class object */
1424         if (VTABLE_is_same(INTERP, SELF, classobj))
1425             goto found;
1427         if (_class->instantiated) {
1428             b = parrot_hash_get_bucket(INTERP, _class->isa_cache,
1429                  (void *)classobj);
1430             if (b)
1431                 return (INTVAL)b->value;
1432         }
1434         /* this is effectively what the default PMC's isa_pmc does
1435          * ... but this can cheat and avoid COW STRINGs for the classobj
1436          * only in these two, very specific and common cases */
1437         if (classobj->vtable->base_type == enum_class_Class
1438         ||  classobj->vtable->base_type == enum_class_PMCProxy) {
1439             STRING *classname = make_class_name(INTERP, classobj);
1440             PARROT_ASSERT(SELF->vtable->isa_hash);
1441             if (parrot_hash_exists(INTERP, SELF->vtable->isa_hash, classname))
1442                 goto found;
1443         }
1445         /* Iterate over all the parents and check if they respond true
1446          * for 'isa' on the original comparison. */
1447         num_classes = VTABLE_elements(INTERP, _class->parents);
1449         for (i = 0; i < num_classes; ++i) {
1450             PMC * const cur_class = VTABLE_get_pmc_keyed_int(INTERP,
1451                     _class->parents, i);
1453             if (VTABLE_isa_pmc(INTERP, cur_class, lookup))
1454                 goto found;
1455         }
1457     cache_and_return:
1458         if (_class->instantiated)
1459             parrot_hash_put(INTERP, _class->isa_cache, (void *)classobj,
1460                  (void *)retval);
1461         return retval;
1463     found:
1464         retval = 1;
1465         goto cache_and_return;
1466     }
1470 =item C<INTVAL isa(STRING *classname)>
1472 Returns whether the class is or inherits from C<*classname>.
1474 =cut
1478     VTABLE INTVAL isa(STRING *classname) {
1479         PMC *want_class;
1481         /* hard-code this one exception right away */
1482         if (Parrot_str_equal(INTERP, classname, CONST_STRING(INTERP, "Class")))
1483             return 1;
1485         want_class = Parrot_oo_get_class_str(INTERP, classname);
1487         if (PMC_IS_NULL(want_class))
1488             return 0;
1490         if (SELF == want_class)
1491             return 1;
1492         else {
1493             Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
1495             INTVAL num_classes = VTABLE_elements(INTERP, _class->all_parents);
1496             int    i           = 0;
1498             for (i = 1; i < num_classes; ++i) {
1499                 PMC * const cur_class = VTABLE_get_pmc_keyed_int(INTERP,
1500                                             _class->all_parents, i);
1502                 if (VTABLE_is_same(INTERP, want_class, cur_class))
1503                     return 1;
1504             }
1505         }
1507         return 0;
1508     }
1512 =item C<INTVAL does(STRING *role_name)>
1514 Returns whether the class does the role with the given C<*role_name>.
1516 =cut
1519     VTABLE INTVAL does(STRING *role_name) {
1520         Parrot_Class_attributes * const _class    = PARROT_CLASS(SELF);
1521         PMC          * const role_list = _class->roles;
1522         INTVAL i, count;
1524         if (!role_list)
1525             return 0;
1527         count = VTABLE_elements(INTERP, role_list);
1529         for (i = 0; i < count; ++i) {
1530             PMC * const role = VTABLE_get_pmc_keyed_int(INTERP, role_list, i);
1532             if (VTABLE_does(INTERP, role, role_name))
1533                 return 1;
1534         }
1536         /* Iterate over all the parents and check if they respond true
1537          * for 'does' on the original comparison. */
1538         count = VTABLE_elements(INTERP, _class->parents);
1540         for (i = 0; i < count; ++i) {
1541             PMC * const cur_class = VTABLE_get_pmc_keyed_int(INTERP,
1542                     _class->parents, i);
1544             if (VTABLE_does(INTERP, cur_class, role_name))
1545                 return 1;
1546         }
1548         return VTABLE_isa(INTERP, SELF, role_name);
1549     }
1553 =item C<INTVAL does_pmc(PMC *role)>
1555 Returns whether the class does the given C<*role>.
1557 =cut
1560     VTABLE INTVAL does_pmc(PMC *role) {
1561         Parrot_Class_attributes * const _class    = PARROT_CLASS(SELF);
1562         PMC * const role_list          = _class->roles;
1563         INTVAL i, role_count, count;
1565         if (!role_list)
1566             return 0;
1568         role_count = VTABLE_elements(INTERP, role_list);
1570         for (i = 0; i < role_count; ++i) {
1571             PMC    * const test_role = VTABLE_get_pmc_keyed_int(INTERP, role_list, i);
1572             if (VTABLE_does_pmc(INTERP, test_role, role))
1573                 return 1;
1574         }
1576         /* Iterate over all the parents and check if they respond true
1577          * for 'does' on the original comparison. */
1578         count = VTABLE_elements(INTERP, _class->parents);
1580         for (i = 0; i < count; ++i) {
1581             PMC * const cur_class = VTABLE_get_pmc_keyed_int(INTERP,
1582                     _class->parents, i);
1584             if (VTABLE_does_pmc(INTERP, cur_class, role))
1585                 return 1;
1586         }
1588         return VTABLE_isa_pmc(INTERP, SELF, role);
1589     }
1593 =item C<INTVAL type()>
1595 Returns the integer type of the class.
1597 =cut
1601     VTABLE INTVAL type() {
1602         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
1603         return _class->id;
1604     }
1608 =item C<void visit(PMC *info)>
1610 This is used by freeze/thaw to visit the contents of the class.
1612 C<*info> is the visit info, (see F<include/parrot/pmc_freeze.h>).
1614 =cut
1618     VTABLE void visit(PMC *info) {
1619         /* 1) visit the attribute description hash */
1620         VISIT_PMC_ATTR(INTERP, info, SELF, Class, attrib_metadata);
1622         /* 2) visit list of parents */
1623         VISIT_PMC_ATTR(INTERP, info, SELF, Class, parents);
1625         /* 3) visit list of roles */
1626         VISIT_PMC_ATTR(INTERP, info, SELF, Class, roles);
1628         /* 4) visit hash of methods */
1629         VISIT_PMC_ATTR(INTERP, info, SELF, Class, methods);
1631         /* 5) visit hash of vtable overrides */
1632         VISIT_PMC_ATTR(INTERP, info, SELF, Class, vtable_overrides);
1634         /* 6) visit list of resolve methods */
1635         VISIT_PMC_ATTR(INTERP, info, SELF, Class, resolve_method);
1636     }
1640 =item C<void freeze(PMC *info)>
1642 Used to archive the class.
1644 =cut
1648     VTABLE void freeze(PMC *info) {
1649         Parrot_Class_attributes * const class_data = PARROT_CLASS(SELF);
1650         STRING       *serial_namespace = CONST_STRING(INTERP, "");
1652         /* 1) freeze class id */
1653         VTABLE_push_integer(INTERP, info, class_data->id);
1655         /* 2) freeze class name */
1656         VTABLE_push_string(INTERP, info, class_data->name);
1658         /* 3) serialize namespace name, including HLL */
1659         if (!PMC_IS_NULL(class_data->_namespace)) {
1660             PMC * const names = Parrot_ns_get_name(INTERP,
1661                     class_data->_namespace);
1662             if (!PMC_IS_NULL(names))
1663                 serial_namespace = Parrot_str_join(INTERP, CONST_STRING(INTERP, ";"), names);
1664         }
1665         VTABLE_push_string(INTERP, info, serial_namespace);
1666     }
1670 =item C<void thaw(PMC *info)>
1672 Used to unarchive the class.
1674 =cut
1678     VTABLE void thaw(PMC *info) {
1679         /* The class might already exist in the interpreter, so create it as an
1680          * anonymous class and later decide whether to link it into the
1681          * namespace.  */
1683         /* 1) thaw class id */
1684         const INTVAL id = VTABLE_shift_integer(INTERP, info);
1686         /* 2) thaw class name */
1687         STRING * const name = VTABLE_shift_string(INTERP, info);
1689         /* 3) deserialize namespace name, including HLL */
1690         STRING * const serial_namespace = VTABLE_shift_string(INTERP, info);
1691         STRING * const semicolon_str = CONST_STRING(INTERP, ";");
1692         PMC    * const namespace_array =
1693             Parrot_str_split(INTERP, semicolon_str, serial_namespace);
1694         PMC *ns = Parrot_ns_get_namespace_keyed(INTERP,
1695                 INTERP->root_namespace, namespace_array);
1697         /* If the namespace doesn't exist, we create it, and initialize
1698          * ourselves in it */
1699         if (PMC_IS_NULL(ns)) {
1700             ns = Parrot_ns_make_namespace_keyed(INTERP,
1701                     INTERP->root_namespace, namespace_array);
1702             SELF.init_pmc(ns);
1703         }
1704         /* If the namespace exists already, we point to it, but otherwise
1705          * act as an anonymous class. */
1706         else {
1707             SELF.init();
1708             PARROT_CLASS(SELF)->_namespace = ns;
1709         }
1711         /* Set the class's short name to the frozen name */
1712         PARROT_CLASS(SELF)->name = name;
1714         /* Set the class's id the frozen id */
1715         PARROT_CLASS(SELF)->id = id;
1716     }
1720 =item C<INTVAL get_integer()>
1722 This is just a temporary hack. Type ID numbers shouldn't be externally
1723 visible to the average PIR user. However, we need this for now to interface
1724 with functions like Parrot_pmc_new and pmc_reuse, which take type ID numbers still.
1726 =cut
1730     VTABLE INTVAL get_integer() {
1731         return PARROT_CLASS(SELF)->id;
1732     }
1736 =item C<void thawfinish(PMC *info)>
1738 Called after the class has been thawed.
1740 =cut
1744     VTABLE void thawfinish(PMC *info) {
1745         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
1746         UNUSED(info)
1748         /* Recalculate full MRO from thawed parents */
1749         _class->all_parents         = Parrot_ComputeMRO_C3(INTERP, SELF);
1750         _class->parent_overrides    = Parrot_pmc_new(INTERP, enum_class_Hash);
1752         /* Rebuild attribute index from thawed attribute metadata */
1753         build_attrib_index(INTERP, SELF);
1754     }
1756     /* **********************************************************************
1757      * Below here are methods that eventually will go in a role
1758      * that is composed into here to optionally give a nice interface from
1759      * PIR (ParrotClass isa Class does ClassMethods or something like this).
1760      * **********************************************************************/
1764 =item C<void name(STRING *name :optional, int has_name :opt_flag)>
1766 Sets the name of the class, and updates the namespace accordingly.
1768 =cut
1771     METHOD name(STRING *name :optional, int has_name :opt_flag) {
1772         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
1773         STRING *ret_name;
1775         if (has_name) {
1776             /* We'll build a hash just containing the name, then give this to
1777              * init_class_from_hash - saves some code duplication. */
1778             PMC    * const naming_hash = Parrot_pmc_new(INTERP, enum_class_Hash);
1779             STRING * const name_str    = CONST_STRING(INTERP, "name");
1781             VTABLE_set_string_keyed_str(INTERP, naming_hash, name_str, name);
1782             init_class_from_hash(INTERP, SELF, naming_hash);
1783         }
1785         ret_name = _class->name;
1786         RETURN(STRING *ret_name);
1787     }
1791 =item C<void get_namespace()>
1793 Gets the namespace that this class is attached to.
1795 =cut
1798     METHOD get_namespace(PMC *_namespace :optional, int has_name :opt_flag) {
1799         Parrot_Class_attributes * const _class        = PARROT_CLASS(SELF);
1800         PMC          * const ret_namespace = _class->_namespace;
1801         UNUSED(_namespace);
1802         UNUSED(has_name);
1803         RETURN(PMC *ret_namespace);
1804     }
1808 =item C<void resolve_method()>
1810 Sets the list of method names that the class provides to resolve conflicts in
1811 methods from roles. When called with no parameter, returns the list.
1813 =cut
1816     METHOD resolve_method(PMC *resolve_list :optional, int has_list :opt_flag) {
1817         Parrot_Class_attributes * const _class   = PARROT_CLASS(SELF);
1818         PMC *ret_list;
1820         /* Store list. */
1821         if (has_list)
1822             _class->resolve_method = resolve_list;
1824         ret_list = _class->resolve_method;
1825         RETURN(PMC *ret_list);
1826     }
1830 =item C<void new(PMC *args :slurpy :named)>
1832 Creates an instance of the object. Initializes any attributes specified in the
1833 parameter list.
1835 =cut
1838     METHOD new(PMC *args :slurpy :named) {
1839         /* Check if any arguments are in the slurpy hash, don't pass an empty
1840          * hash to instantiate */
1841         PMC * const obj =
1842             VTABLE_elements(INTERP, args) > 0
1843                 ? VTABLE_instantiate(INTERP, SELF, args)
1844                 : VTABLE_instantiate(INTERP, SELF, PMCNULL);
1846         RETURN(PMC *obj);
1847      }
1851 =item C<void attributes()>
1853 Return a hash where the keys are attribute names and the values are hashes
1854 providing a set of key/value pairs describing the attribute.
1856 =cut
1859     METHOD attributes() {
1860         STRING * const attr_str            = CONST_STRING(INTERP, "attributes");
1861         PMC    * const ret_attrib_metadata = SELF.inspect_str(attr_str);
1863         RETURN(PMC *ret_attrib_metadata);
1864     }
1868 =item C<void add_attribute()>
1870 Add an attribute to the class. Requires a name and, optionally, a type.
1872 =cut
1875     METHOD add_attribute(STRING *attribute_name,
1876             PMC *attribute_type :optional, int has_type :opt_flag) {
1877         PMC * const type = has_type ? attribute_type : PMCNULL;
1878         SELF.add_attribute(attribute_name, type);
1879     }
1883 =item C<void methods()>
1885 Return a hash where the keys are method names and the values are methods.
1887 =cut
1890     METHOD methods() {
1891         PMC * const ret_methods = SELF.inspect_str(CONST_STRING(INTERP, "methods"));
1893         RETURN(PMC *ret_methods);
1894     }
1898 =item C<void add_method(STRING *name, PMC *sub)>
1900 Adds the given sub PMC as a method with the given name. Delegates to the
1901 C<add_method> vtable.
1903 =cut
1906     METHOD add_method(STRING *name, PMC *sub) {
1907         SELF.add_method(name, sub);
1908     }
1912 =item C<void add_vtable_override(STRING *name, PMC *sub)>
1914 Adds the given sub PMC as a vtable override with the given name. Delegates to
1915 the C<add_vtable_override> vtable.
1917 =cut
1920     METHOD add_vtable_override(STRING *name, PMC *sub) {
1921         VTABLE_add_vtable_override(INTERP, SELF, name, sub);
1922     }
1926 =item C<void remove_method(STRING *name)>
1928 Removes the method with the given name.
1930 =cut
1933     METHOD remove_method(STRING *name) {
1934         VTABLE_remove_method(INTERP, SELF, name);
1935     }
1939 =item C<PMC *find_method(STRING *name)>
1941 Walks the MRO of the class and finds the method with the given name.
1943 =cut
1947     METHOD find_method(STRING *name) {
1948         Parrot_Class_attributes * const  _class    = PARROT_CLASS(SELF);
1949         int i;
1951         /* Walk and search. One day, we'll use the cache first. */
1952         const int num_classes = VTABLE_elements(INTERP, _class->all_parents);
1954         for (i = 0; i < num_classes; ++i) {
1955             /* Get the class and see if it has the method. */
1956             PMC * const cur_class =
1957                 VTABLE_get_pmc_keyed_int(INTERP, _class->all_parents, i);
1958             const Parrot_Class_attributes * const class_info = PARROT_CLASS(cur_class);
1960             /* Found it! */
1961             if (VTABLE_exists_keyed_str(INTERP, class_info->methods, name)) {
1962                 PMC * const ret = VTABLE_get_pmc_keyed_str(INTERP, class_info->methods, name);
1963                 RETURN(PMC *ret);
1964             }
1965         }
1967         RETURN(PMC *PMCNULL);
1968     }
1972 =item C<void parents()>
1974 Returns the parents array PMC.
1976 =cut
1979     METHOD parents() {
1980         PMC * const ret_parents = SELF.inspect_str(CONST_STRING(INTERP, "parents"));
1982         RETURN(PMC *ret_parents);
1983     }
1987 =item C<void add_parent(PMC *parent)>
1989 Adds the supplied PMC to the list of parents for the class.
1991 =cut
1994     METHOD add_parent(PMC *parent) {
1995         SELF.add_parent(parent);
1996     }
2000 =item C<void roles()>
2002 Returns the roles array PMC.
2004 =cut
2007     METHOD roles() {
2008         PMC * const ret_roles = SELF.inspect_str(CONST_STRING(INTERP, "roles"));
2010         RETURN(PMC *ret_roles);
2011     }
2015 =item C<void add_role(PMC *role, PMC *exclude :optional :named("exclude"),
2016 PMC *alias :optional :named("alias"))>
2018 Composes a role into a class with the given exclusions and aliases.
2020 =cut
2023     METHOD add_role(PMC *role,
2024             PMC *exclude_method     :optional :named("exclude_method"),
2025             int has_exclude_method  :opt_flag,
2026             PMC *alias_method       :optional :named("alias_method"),
2027             int has_alias_method    :opt_flag) {
2029         Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
2031         /* Add everything on the resolve list to the exclude list; if we have
2032          * no exclude list, pass along the resolve list in its place if it has
2033          * any methods listed in it. */
2034         if (!has_exclude_method) {
2035             if (VTABLE_elements(INTERP, _class->resolve_method) != 0) {
2036                 exclude_method     = _class->resolve_method;
2037                 has_exclude_method = 1;
2038             }
2039         }
2040         else {
2041             const int resolve_count = VTABLE_elements(INTERP, _class->resolve_method);
2042             int i;
2044             for (i = 0; i < resolve_count; ++i) {
2045                 STRING * const meth_name = VTABLE_get_string_keyed_int(INTERP,
2046                     _class->resolve_method, i);
2047                 VTABLE_push_string(INTERP, exclude_method, meth_name);
2048             }
2049         }
2051         /* Do the composition. */
2052         Parrot_ComposeRole(INTERP, role, exclude_method, has_exclude_method,
2053                            alias_method, has_alias_method,
2054                            _class->methods, _class->roles);
2055     }
2059 =item C<void inspect(STRING *what :optional)>
2061 Gets all introspection data for the class or, if the optional string
2062 parameter is supplied, a particular item of introspection data.
2064 =cut
2067     METHOD inspect(STRING *what :optional, int has_what :opt_flag) {
2068         PMC *found;
2070         /* Just delegate to the appropriate vtable. */
2071         if (has_what)
2072             found = SELF.inspect_str(what);
2073         else
2074             found = SELF.inspect();
2076         RETURN(PMC *found);
2077     }
2081 =item C<void isa(STRING *class_name)>
2083 Returns true if this object is or derives from the class named in
2084 C<class_name>, false otherwise.
2086 =cut
2089     METHOD isa(STRING *class_name) {
2090         const INTVAL isa = SELF.isa(class_name);
2092         RETURN(INTVAL isa);
2093     }
2097 =item C<void does(STRING *role_name)>
2099 Returns true if this object or one of its parents performs the named role,
2100 false otherwise.
2102 =cut
2105     METHOD does(STRING *role_name) {
2106         const INTVAL does = VTABLE_does(INTERP, SELF, role_name);
2107         RETURN(INTVAL does);
2108     }
2110     METHOD clear_method_cache() {
2111         Parrot_Class_attributes * const attrs = PARROT_CLASS(SELF);
2112         PMC * const cache = attrs->meth_cache;
2113         if (cache)
2114             attrs->meth_cache = PMCNULL;
2115     }
2117     METHOD get_method_cache() {
2118         Parrot_Class_attributes * const attrs = PARROT_CLASS(SELF);
2119         PMC * cache = attrs->meth_cache;
2120         if (!cache) {
2121             cache = Parrot_pmc_new(INTERP, enum_class_Hash);
2122             attrs->meth_cache = cache;
2123         }
2124         RETURN(PMC *cache);
2125     }
2127 } /* END pmclass */
2131 =back
2133 =head1 SEE ALSO
2135 F<docs/pdds/pdd15_objects.pod>.
2137 =cut
2142  * Local variables:
2143  *   c-file-style: "parrot"
2144  * End:
2145  * vim: expandtab shiftwidth=4:
2146  */