1/*
2Copyright (C) 2005-2014, Parrot Foundation.
3
4=head1 NAME
5
6src/pmc/namespace.pmc - NameSpace PMC
7
8=head1 DESCRIPTION
9
10These are the vtable functions for the namespace PMC,
11a container for dynamic and structured variable lookup.
12
13=head2 Functions
14
15=over 4
16
17=cut
18
19*/
20
21#include "pmc/pmc_sub.h"
22
23/* HEADERIZER HFILE: none */
24/* HEADERIZER BEGIN: static */
25/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
26
27static void add_multi_to_namespace(PARROT_INTERP,
28    ARGIN(PMC *SELF),
29    ARGIN(STRING *key),
30    ARGIN_NULLOK(PMC *value))
31        __attribute__nonnull__(1)
32        __attribute__nonnull__(2)
33        __attribute__nonnull__(3);
34
35static void add_native_to_namespace(PARROT_INTERP,
36    ARGIN(PMC *SELF),
37    ARGIN(STRING *key),
38    ARGIN_NULLOK(PMC *value))
39        __attribute__nonnull__(1)
40        __attribute__nonnull__(2)
41        __attribute__nonnull__(3);
42
43static void add_to_class(PARROT_INTERP,
44    ARGMOD(Parrot_NameSpace_attributes *nsinfo),
45    ARGMOD_NULLOK(PMC *classobj),
46    ARGIN(STRING *key),
47    ARGIN(PMC *value))
48        __attribute__nonnull__(1)
49        __attribute__nonnull__(2)
50        __attribute__nonnull__(4)
51        __attribute__nonnull__(5)
52        FUNC_MODIFIES(*nsinfo)
53        FUNC_MODIFIES(*classobj);
54
55PARROT_WARN_UNUSED_RESULT
56static int maybe_add_sub_to_namespace(PARROT_INTERP,
57    ARGIN(PMC *SELF),
58    ARGIN(STRING *key),
59    ARGIN(PMC *value))
60        __attribute__nonnull__(1)
61        __attribute__nonnull__(2)
62        __attribute__nonnull__(3)
63        __attribute__nonnull__(4);
64
65PARROT_WARN_UNUSED_RESULT
66static int ns_insert_sub_keyed_str(PARROT_INTERP,
67    ARGIN(PMC *self),
68    ARGIN(STRING *key),
69    ARGIN(PMC *value))
70        __attribute__nonnull__(1)
71        __attribute__nonnull__(2)
72        __attribute__nonnull__(3)
73        __attribute__nonnull__(4);
74
75#define ASSERT_ARGS_add_multi_to_namespace __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
76       PARROT_ASSERT_ARG(interp) \
77    , PARROT_ASSERT_ARG(SELF) \
78    , PARROT_ASSERT_ARG(key))
79#define ASSERT_ARGS_add_native_to_namespace __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
80       PARROT_ASSERT_ARG(interp) \
81    , PARROT_ASSERT_ARG(SELF) \
82    , PARROT_ASSERT_ARG(key))
83#define ASSERT_ARGS_add_to_class __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
84       PARROT_ASSERT_ARG(interp) \
85    , PARROT_ASSERT_ARG(nsinfo) \
86    , PARROT_ASSERT_ARG(key) \
87    , PARROT_ASSERT_ARG(value))
88#define ASSERT_ARGS_maybe_add_sub_to_namespace __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
89       PARROT_ASSERT_ARG(interp) \
90    , PARROT_ASSERT_ARG(SELF) \
91    , PARROT_ASSERT_ARG(key) \
92    , PARROT_ASSERT_ARG(value))
93#define ASSERT_ARGS_ns_insert_sub_keyed_str __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
94       PARROT_ASSERT_ARG(interp) \
95    , PARROT_ASSERT_ARG(self) \
96    , PARROT_ASSERT_ARG(key) \
97    , PARROT_ASSERT_ARG(value))
98/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
99/* HEADERIZER END: static */
100
101/*
102
103=item C<static void add_to_class(PARROT_INTERP, Parrot_NameSpace_attributes
104*nsinfo, PMC *classobj, STRING *key, PMC *value)>
105
106Add the method Sub C<value> with name C<key> to the C<classobj>.
107
108=cut
109
110*/
111
112static void
113add_to_class(PARROT_INTERP, ARGMOD(Parrot_NameSpace_attributes *nsinfo),
114        ARGMOD_NULLOK(PMC *classobj), ARGIN(STRING *key), ARGIN(PMC *value))
115{
116    ASSERT_ARGS(add_to_class)
117
118    /* Insert it in class, if there is a class */
119    if (!PMC_IS_NULL(classobj) && PObj_is_class_TEST(classobj))
120        VTABLE_add_method(interp, classobj, key, value);
121
122    /* Otherwise, store it in the namespace for the class to
123     * retrieve later */
124    else {
125        /* If we don't have a place to hang methods, make one. */
126        if (PMC_IS_NULL(nsinfo->methods))
127            nsinfo->methods = Parrot_pmc_new(interp, enum_class_Hash);
128
129        /* Insert it. */
130        VTABLE_set_pmc_keyed_str(interp, nsinfo->methods, key, value);
131    }
132}
133
134/*
135
136=item C<static int ns_insert_sub_keyed_str(PARROT_INTERP, PMC *self, STRING
137*key, PMC *value)>
138
139Add the Sub PMC C<value> to the namespace. If it's a vtable, add it as a
140vtable to the corresponding Class (or store it locally for the Class to fetch
141later). If it's a method, do the same.
142
143=cut
144
145*/
146
147PARROT_WARN_UNUSED_RESULT
148static int
149ns_insert_sub_keyed_str(PARROT_INTERP, ARGIN(PMC *self), ARGIN(STRING *key),
150        ARGIN(PMC *value))
151{
152    ASSERT_ARGS(ns_insert_sub_keyed_str)
153
154    Parrot_NameSpace_attributes * const nsinfo   = PARROT_NAMESPACE(self);
155    PMC              *       vtable   = nsinfo->vtable;
156    PMC              * const classobj = VTABLE_get_class(interp, self);
157    STRING           * vtable_key     = STRINGNULL;
158    Parrot_Sub_attributes *sub;
159    INTVAL stored = 0;
160
161    PMC_get_sub(interp, value, sub);
162
163    if (sub->vtable_index != -1) {
164        /* Insert it in class, if there is a class */
165        if (!PMC_IS_NULL(classobj) && PObj_is_class_TEST(classobj)) {
166            const char * const vtable_key_c =
167                Parrot_get_vtable_name(interp, sub->vtable_index);
168            PARROT_ASSERT(vtable_key_c);
169            vtable_key = Parrot_str_new(interp, vtable_key_c,
170                strlen(vtable_key_c));
171            VTABLE_add_vtable_override(interp, classobj, vtable_key, value);
172        }
173
174        /* Otherwise, store it in the namespace for the class to
175         * retrieve later */
176        else {
177            /* If we don't have a place to hang vtables, make one. */
178            if (PMC_IS_NULL(vtable))
179                nsinfo->vtable = vtable = Parrot_pmc_new(interp, enum_class_Hash);
180
181            /* Insert it. */
182            VTABLE_set_pmc_keyed_int(interp, vtable, sub->vtable_index, value);
183        }
184        if (!(sub->comp_flags & SUB_COMP_FLAG_NSENTRY))
185            stored = 1;
186    }
187
188    if (sub->comp_flags & SUB_COMP_FLAG_METHOD) {
189        STRING *method_name = key;
190
191        if (STRING_equal(interp, sub->method_name, CONST_STRING(interp, ""))) {
192            if (sub->vtable_index != -1 && !STRING_IS_NULL(vtable_key)) {
193                method_name = vtable_key;
194            }
195        }
196        else {
197            method_name = sub->method_name;
198        }
199        add_to_class(interp, nsinfo, classobj, method_name, value);
200
201        if (!(sub->comp_flags & SUB_COMP_FLAG_NSENTRY))
202            stored = 1;
203    }
204
205    return stored;
206}
207
208/*
209
210=item C<static int maybe_add_sub_to_namespace(PARROT_INTERP, PMC *SELF, STRING
211*key, PMC *value)>
212
213If the item C<value> is a non-null Sub PMC (but not a subclass), add it to the
214namespace and any associated Class PMC (if it's a method or vtable).
215
216=cut
217
218*/
219
220PARROT_WARN_UNUSED_RESULT
221static int
222maybe_add_sub_to_namespace(PARROT_INTERP, ARGIN(PMC *SELF), ARGIN(STRING *key),
223        ARGIN(PMC *value))
224{
225    ASSERT_ARGS(maybe_add_sub_to_namespace)
226
227    STRING * const sub_str = CONST_STRING(interp, "Sub");
228
229    if (!PMC_IS_NULL(value)
230    &&   VTABLE_isa(interp, value, sub_str)
231    &&   value->vtable->base_type != enum_class_Object)
232        return ns_insert_sub_keyed_str(interp, SELF, key, value);
233
234    return 0;
235}
236
237/*
238
239=item C<static void add_native_to_namespace(PARROT_INTERP, PMC *SELF, STRING
240*key, PMC *value)>
241
242Add an NCI or NativePCCMethod PMC to the namespace. The PMC C<value> must
243be non-null. If it's flagged as a method (usually for NativePCCMethod PMCs)
244add it to the Class as well.
245
246=cut
247
248*/
249
250static void
251add_native_to_namespace(PARROT_INTERP, ARGIN(PMC *SELF), ARGIN(STRING *key),
252        ARGIN_NULLOK(PMC *value))
253{
254    ASSERT_ARGS(add_native_to_namespace)
255
256    if (!PMC_IS_NULL(value)
257    && (value->vtable->base_type == enum_class_NativePCCMethod ||
258        value->vtable->base_type == enum_class_NCI)) {
259        Parrot_NameSpace_attributes * const nsinfo = PARROT_NAMESPACE(SELF);
260        PMC * const classobj = VTABLE_get_class(interp, SELF);
261
262        /* Insert it in class, if there is a class */
263        add_to_class(interp, nsinfo, classobj, key, value);
264    }
265}
266
267/*
268
269=item C<static void add_multi_to_namespace(PARROT_INTERP, PMC *SELF, STRING
270*key, PMC *value)>
271
272If PMC C<value> is a MultiSub, add it to the namespace and possibly the
273associated class.
274
275=cut
276
277*/
278
279static void
280add_multi_to_namespace(PARROT_INTERP, ARGIN(PMC *SELF), ARGIN(STRING *key),
281        ARGIN_NULLOK(PMC *value))
282{
283    ASSERT_ARGS(add_multi_to_namespace)
284
285    STRING * const multi_str = CONST_STRING(interp, "MultiSub");
286
287    if (!PMC_IS_NULL(value)
288    &&   VTABLE_isa(interp, value, multi_str)) {
289
290        /* TT #10; work around that Sub doesn't use PMC ATTRs */
291        if (value->vtable->base_type != enum_class_Object
292        &&  VTABLE_elements(interp, value) > 0) {
293            Parrot_NameSpace_attributes * const nsinfo = PARROT_NAMESPACE(SELF);
294            PMC * const classobj = VTABLE_get_class(interp, SELF);
295
296            /* Extract the first alternate and check if it is a method */
297            PMC * const pmc_sub = VTABLE_get_pmc_keyed_int(interp, value, 0);
298            Parrot_Sub_attributes *sub;
299            PMC_get_sub(interp, pmc_sub, sub);
300
301            if (sub->comp_flags & SUB_COMP_FLAG_METHOD) {
302                STRING * const empty_str   = CONST_STRING(interp, "");
303                STRING *method_name = key;
304                Hash   *hash;
305
306                if (!STRING_equal(interp, sub->method_name, empty_str))
307                    method_name = sub->method_name;
308
309                add_to_class(interp, nsinfo, classobj, method_name, value);
310
311                GETATTR_NameSpace_hash(interp, SELF, hash);
312                PARROT_GC_WRITE_BARRIER(interp, SELF);
313
314                Parrot_hash_put(interp, hash,
315                    Parrot_hash_key_from_string(interp, hash, key),
316                    Parrot_hash_value_from_pmc(interp, hash, value));
317            }
318        }
319    }
320}
321
322
323/*
324 * Typically a named slot contains either another namespace or a
325 * var/sub (not both).
326 * In case that the bucket->value is occupied, a FixedPMCArray is
327 * created, and the items are moved over to that extra storage.
328 * The array is flagged with FPA_is_ns_ext to distinguish it from a
329 * plain array variable.
330 *
331 * This could easily expand to a full-fledged typed namespace if needed.
332 */
333
334typedef enum {
335    NS_slot_ns,
336    NS_slot_var_sub,         /* unspecified ~half-raw slot */
337    NS_max_slots
338} NS_slot_enum;
339
340#define FPA_is_ns_ext PObj_private0_FLAG
341
342pmclass NameSpace extends Hash provides hash no_ro auto_attrs {
343
344    ATTR STRING *name;     /* Name of this namespace part. */
345    ATTR PMC    *_class;   /* The class or role attached to this namespace. */
346    ATTR PMC    *methods;  /* A Hash of methods, keyed on the method name. This
347                            * goes away when the methods are sucked in by a
348                            * class. */
349    ATTR PMC    *vtable;   /* A Hash of vtable subs, keyed on the vtable index */
350    ATTR PMC    *parent;   /* This NameSpace's parent NameSpace */
351
352/*
353
354=item C<void init()>
355
356Initialize a C<NameSpace> PMC.
357
358=cut
359
360*/
361
362    VTABLE void init() {
363        SUPER();
364        PARROT_NAMESPACE(SELF)->vtable  = PMCNULL;
365        PARROT_NAMESPACE(SELF)->methods = PMCNULL;
366        PARROT_NAMESPACE(SELF)->_class  = PMCNULL;
367        PObj_custom_mark_SET(SELF);
368    }
369
370/*
371
372=item C<void mark()>
373
374Marks the namespace as live.
375
376=cut
377
378*/
379    VTABLE void mark() :no_wb {
380        Parrot_NameSpace_attributes * const nsinfo = PARROT_NAMESPACE(SELF);
381
382        SUPER();
383        Parrot_gc_mark_PMC_alive(INTERP, nsinfo->parent);
384        Parrot_gc_mark_PMC_alive(INTERP, nsinfo->_class);
385        Parrot_gc_mark_PMC_alive(INTERP, nsinfo->vtable);
386        Parrot_gc_mark_PMC_alive(INTERP, nsinfo->methods);
387        Parrot_gc_mark_STRING_alive(INTERP, nsinfo->name);
388    }
389
390/*
391
392=item C<PMC *get_class()>
393
394Returns the class or role PMC that is associated with this namespace.
395
396=cut
397
398*/
399
400    PMC *get_class() :no_wb {
401        UNUSED(INTERP);
402        return PARROT_NAMESPACE(SELF)->_class;
403    }
404
405/*
406
407=item C<void set_pmc_keyed_str(STRING *key, PMC *value)>
408
409Sets C<*value> as the namespace item for C<*key>. This is part of the
410raw interface. If the PMC C<value> is exactly a NameSpace, C<SELF>
411will be set as the parent of that namespace and the name C<key> of
412C<value> is stored too.
413
414=item C<void set_pmc_keyed(PMC *key, PMC *value)>
415
416If C<key> is a simple key, it works like above. If C<key> is an array
417of strings or a chained key, add all components to the namespace.
418
419=item C<PMC *get_pmc_keyed(PMC *key)>
420
421Return the given namespace or PMCNULL. C<key> is either an array of
422strings, or a possibly nested key.
423
424=item C<PMC *get_pmc_keyed_str(STRING *key)>
425
426Return the given namespace item or PMCNULL. If the named item is either
427a NameSpace or a var, the NameSpace is returned.
428
429=item C<PMC *get_pmc_keyed_int(INTVAL key)>
430
431Return a Sub representing an overridden vtable entry or PMCNULL.  This is not
432really a public API.
433
434=cut
435
436*/
437
438    VTABLE void set_pmc_keyed_str(STRING *key, PMC *value) {
439        PMC        *new_tuple = NULL;
440        const int   val_is_NS = PMC_IS_NULL(value)
441                              ? 0
442                              :value->vtable->base_type == enum_class_NameSpace;
443
444        /* don't need this everywhere yet */
445        PMC *old;
446
447        /* If it's a sub... */
448        if (maybe_add_sub_to_namespace(INTERP, SELF, key, value))
449            return;
450
451        /* If it's an native method */
452        add_native_to_namespace(INTERP, SELF, key, value);
453
454        /* If it's a multi-sub and the first in this NS... */
455        add_multi_to_namespace(INTERP, SELF, key, value);
456
457        old = (PMC *)Parrot_hash_get(INTERP, (Hash *)SELF.get_pointer(), key);
458
459        if (!old)
460            SUPER(key, value);
461        else {
462            if ((old->vtable->base_type == enum_class_NameSpace) == val_is_NS) {
463                /* simple ns or simple var/sub changed */
464                SUPER(key, value);
465            }
466            else if ((PObj_get_FLAGS(old) & FPA_is_ns_ext) &&
467                    old->vtable->base_type == enum_class_FixedPMCArray) {
468                /* we have a tuple extension already */
469                VTABLE_set_pmc_keyed_int(INTERP, old,
470                        val_is_NS ? NS_slot_ns : NS_slot_var_sub,
471                        value);
472            }
473            else {
474                /* create new tuple */
475                /* for a fully typed namespace, we'd need 3 or 4 */
476                new_tuple = Parrot_pmc_new_init_int(INTERP,
477                        enum_class_FixedPMCArray, NS_max_slots);
478
479                /* flag it as special */
480                PObj_get_FLAGS(new_tuple) |= FPA_is_ns_ext;
481            }
482        }
483
484        if (val_is_NS) {
485            /* TODO - this hack needs to go */
486            Parrot_NameSpace_attributes * const nsinfo = PARROT_NAMESPACE(value);
487            nsinfo->parent = SELF;  /* set parent */
488            nsinfo->name   = key;   /* and name */
489
490            if (new_tuple) {
491                VTABLE_set_pmc_keyed_int(INTERP, new_tuple, NS_slot_ns, value);
492                VTABLE_set_pmc_keyed_int(INTERP, new_tuple, NS_slot_var_sub,
493                                         old);
494
495                Parrot_hash_put(INTERP, (Hash *)SELF.get_pointer(), key, new_tuple);
496                /* distinction from a plain FPA, which doesn't extend the
497                 * namespace storage */
498            }
499        }
500        else if (new_tuple) {
501            VTABLE_set_pmc_keyed_int(INTERP, new_tuple, NS_slot_ns, old);
502            VTABLE_set_pmc_keyed_int(INTERP, new_tuple, NS_slot_var_sub, value);
503            Parrot_hash_put(INTERP, (Hash *)SELF.get_pointer(), key, new_tuple);
504        }
505    }
506
507    VTABLE void set_pmc_keyed(PMC *key, PMC *value) :manual_wb {
508        PMC *ns = SELF;
509
510        if (key->vtable->base_type == enum_class_String) {
511            SELF.set_pmc_keyed_str(VTABLE_get_string(INTERP, key), value);
512            return;
513        }
514
515        if (key->vtable->base_type == enum_class_Key) {
516            while (1) {
517                STRING * const part = VTABLE_get_string(INTERP, key);
518                key                 = VTABLE_shift_pmc(INTERP, key);
519
520                if (!key) {
521                    Parrot_ns_set_global(INTERP, ns, part, value);
522                    PARROT_GC_WRITE_BARRIER(INTERP, SELF);
523                    return;
524                }
525
526                ns = Parrot_ns_make_namespace_keyed_str(INTERP, ns, part);
527            }
528        }
529
530        if (key->vtable->base_type == enum_class_ResizableStringArray) {
531            const INTVAL elements = VTABLE_elements(INTERP, key);
532            INTVAL i;
533            for (i = 0; i < elements; ++i)  {
534                STRING * const part = VTABLE_get_string_keyed_int(INTERP, key, i);
535
536                if ((i + 1) >= elements) { /* Last entry in the array */
537                    Parrot_ns_set_global(INTERP, ns, part, value);
538                    PARROT_GC_WRITE_BARRIER(INTERP, SELF);
539                    return;
540                }
541
542                ns = Parrot_ns_make_namespace_keyed_str(INTERP, ns, part);
543            }
544        }
545
546        Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_GLOBAL_NOT_FOUND,
547            "Invalid namespace key in set_pmc_keyed");
548    }
549
550    VTABLE PMC *get_pmc_keyed_str(STRING *key) :no_wb {
551        Hash * const hash = (Hash *)SELF.get_pointer();
552        PMC *ns = (PMC *)Parrot_hash_get(INTERP, hash, key);
553
554        if (!ns)
555            return PMCNULL;
556
557        if ((PObj_get_FLAGS(ns) & FPA_is_ns_ext) &&
558                ns->vtable->base_type == enum_class_FixedPMCArray)
559            ns = VTABLE_get_pmc_keyed_int(INTERP, ns, NS_slot_ns);
560
561        return ns;
562    }
563
564    VTABLE PMC *get_pmc_keyed(PMC *key) :no_wb {
565        PMC *ns = SUPER(key);
566        /* Is this equivalent?
567        PMC    *ns = INTERP->vtables[enum_class_Hash]->get_pmc_keyed(INTERP, SELF, key);
568        */
569
570        if (!PMC_IS_NULL(ns))
571            return ns;
572
573        ns = SELF;
574
575        if (key->vtable->base_type == enum_class_Key) {
576            STRING * const part = VTABLE_get_string(INTERP, key);
577            key                 = VTABLE_shift_pmc(INTERP, key);
578
579            if (!key)
580                return VTABLE_get_pmc_keyed_str(INTERP, ns, part);
581
582            ns = Parrot_ns_get_namespace_keyed_str(INTERP, ns, part);
583
584            if (PMC_IS_NULL(ns))
585                return PMCNULL;
586
587            return VTABLE_get_pmc_keyed(INTERP, ns, key);
588        }
589        else if (VTABLE_does(INTERP, key, CONST_STRING(INTERP, "array"))) {
590            const INTVAL elements = VTABLE_elements(INTERP, key);
591            INTVAL i;
592            for (i = 0; i < elements && !PMC_IS_NULL(ns); ++i) {
593                STRING * const name = VTABLE_get_string_keyed_int(INTERP, key, i);
594                if (STRING_IS_NULL(name)) {
595                    ns = PMCNULL;
596                    /* What to do here? Throw an exception or something? */
597                    break;
598                }
599                ns = Parrot_ns_get_namespace_keyed_str(INTERP, ns, name);
600            }
601            return ns;
602        }
603        else {
604            STRING * const name = VTABLE_get_string(INTERP, key);
605            ns = Parrot_ns_get_namespace_keyed_str(INTERP, ns, name);
606            return ns;
607        }
608        /* If we get the wrong type, should we throw an exception?
609        Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_GLOBAL_NOT_FOUND,
610            "Invalid namespace key in get_pmc_keyed_str");
611        */
612    }
613
614    VTABLE PMC *get_pmc_keyed_int(INTVAL key) :no_wb {
615        Parrot_NameSpace_attributes * const nsinfo = PARROT_NAMESPACE(SELF);
616        PMC              * const vtable = nsinfo->vtable;
617
618        if (PMC_IS_NULL(vtable))
619            return PMCNULL;
620
621        return VTABLE_get_pmc_keyed_int(INTERP, vtable, key);
622    }
623
624/*
625
626=item C<void *get_pointer_keyed_str(STRING *key)>
627
628Return the given namepace item or PMCNULL, keyed by name.
629
630=item C<void *get_pointer_keyed(PMC *key)>
631
632Return the given namespace item or PMCNULL. If the named item is either
633a NameSpace or a var, the var is returned.
634
635TT #1472
636TOTAL KLUDGE.  ON THE CHOPPING BLOCK.
637
638=cut
639
640*/
641
642    VTABLE void *get_pointer_keyed_str(STRING *key) :no_wb {
643        PMC  *ns;
644        Hash *hash;
645        GETATTR_NameSpace_hash(INTERP, SELF, hash);
646
647        ns = (PMC *)Parrot_hash_get(INTERP, hash, key);
648
649        if (ns && (PObj_get_FLAGS(ns) & FPA_is_ns_ext)
650            && ns->vtable->base_type == enum_class_FixedPMCArray)
651            ns = VTABLE_get_pmc_keyed_int(INTERP, ns, NS_slot_var_sub);
652
653        /* Be extra careful about returning PMCNULL */
654        if (!ns) ns = PMCNULL;
655        RETURN(PMC *ns);
656    }
657
658    VTABLE void *get_pointer_keyed(PMC *key) :no_wb {
659        PMC    *ns = SELF;
660
661        if (PMC_IS_NULL(key))
662            return PMCNULL;
663
664        switch (key->vtable->base_type) {
665            case enum_class_String:
666                return SELF.get_pointer_keyed_str(VTABLE_get_string(INTERP, key));
667            case enum_class_Key: {
668                /* this loop (and function) could use a rewrite for clarity */
669                while (1) {
670                    STRING * const part = VTABLE_get_string(INTERP, key);
671                    key                 = VTABLE_shift_pmc(INTERP, key);
672
673                    if (!key)
674                        return VTABLE_get_pointer_keyed_str(INTERP, ns, part);
675
676                    ns = Parrot_ns_get_namespace_keyed_str(INTERP, ns, part);
677
678                    if (PMC_IS_NULL(ns))
679                        return PMCNULL;
680                }
681            }
682            case enum_class_ResizableStringArray: {
683                const INTVAL elements = VTABLE_elements(INTERP, key);
684                INTVAL i;
685                for (i = 0; i < elements; ++i)  {
686                    STRING * const part = VTABLE_get_string_keyed_int(INTERP, key, i);
687
688                    /* Last entry in the array */
689                    if ((i + 1) >= elements)
690                        return VTABLE_get_pointer_keyed_str(INTERP, ns, part);
691
692                    ns = Parrot_ns_get_namespace_keyed_str(INTERP, ns, part);
693
694                    if (PMC_IS_NULL(ns))
695                        return PMCNULL;
696                }
697                return ns;
698            }
699            default:
700                Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_GLOBAL_NOT_FOUND,
701                    "Invalid namespace key of type '%S' in get_pointer_keyed",
702                    key->vtable->whoami);
703        }
704    }
705
706/*
707
708=item C<STRING *get_string()>
709
710Return the name of this namespace part.
711
712=cut
713
714*/
715
716    VTABLE STRING *get_string() :no_wb {
717        const Parrot_NameSpace_attributes * const nsinfo = PARROT_NAMESPACE(SELF);
718        UNUSED(INTERP)
719        return nsinfo->name;
720    }
721
722/*
723
724=item C<PMC *inspect_str(STRING *what)>
725
726Provides introspection of a specific piece of information about the
727namespace. The available information is:
728
729=over 8
730
731=item class
732
733The class object associated with the namespace, if any.
734
735=item methods
736
737A temporary cache of methods (destroyed when class object is created).
738Hash keyed on method name, value is an invokable PMC. Includes methods
739composed in from roles.
740
741=item vtable_overrides
742
743A temporary cache of vtable overrides (destroyed when class object is
744created). Hash keyed on vtable name, value is an invokable PMC.
745Includes vtable overrides composed in from roles.
746
747=back
748
749=cut
750
751*/
752
753    VTABLE PMC *inspect_str(STRING *what) :no_wb {
754        const Parrot_NameSpace_attributes * const nsinfo = PARROT_NAMESPACE(SELF);
755        PMC *found;
756
757        if (STRING_equal(INTERP, what, CONST_STRING(INTERP, "methods"))) {
758            found = nsinfo->methods;
759        }
760        else if (STRING_equal(INTERP, what, CONST_STRING(INTERP, "vtable_overrides"))) {
761            found = nsinfo->vtable;
762        }
763        else if (STRING_equal(INTERP, what, CONST_STRING(INTERP, "class"))) {
764            found = nsinfo->_class;
765        }
766        else
767            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
768                "Unknown introspection value '%S'", what);
769
770        /* return found value */
771        if (PMC_IS_NULL(found))
772            return PMCNULL;
773
774        return found;
775    }
776
777/*
778
779=back
780
781=head2 Methods
782
783=over 4
784
785=cut
786
787=item C<METHOD make_namespace(PMC* key)>
788
789Create and retrieve the namespace given by C<key>.  If the namespace
790already exists, only retrieve it.
791
792=cut
793
794*/
795
796    METHOD make_namespace(PMC *key) {
797        PMC *ns = Parrot_ns_get_namespace_keyed(INTERP, SELF, key);
798        if (PMC_IS_NULL(ns)) {
799            ns = Parrot_ns_make_namespace_keyed(INTERP, SELF, key);
800        }
801        RETURN(PMC *ns);
802    }
803
804/*
805
806=item C<METHOD add_namespace(STRING *name, PMC *namespace)>
807
808Stores the given namespace under this namespace, with the given name.  Throws
809an invalid type exception if C<namespace> is not a NameSpace PMC or subclass.
810
811=cut
812
813*/
814
815    METHOD add_namespace(STRING *name, PMC *_namespace) :manual_wb {
816        STRING * const s_ns = CONST_STRING(INTERP, "NameSpace");
817
818        if (!VTABLE_isa(INTERP, _namespace, s_ns))
819            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
820                "Invalid type %d in add_namespace()",
821                _namespace->vtable->base_type);
822
823        VTABLE_set_pmc_keyed_str(INTERP, SELF, name, _namespace);
824    }
825
826/*
827
828=item C<METHOD add_sub(STRING *name, PMC *sub)>
829
830Stores the given sub under this namespace, with the given name.  Throws an
831invalid type exception if C<sub> is not a Sub PMC or subclass.
832
833=cut
834
835*/
836
837    METHOD add_sub(STRING *name, PMC *sub) :manual_wb {
838        STRING * const s_sub      = CONST_STRING(INTERP, "Sub");
839        STRING * const s_multisub = CONST_STRING(INTERP, "MultiSub");
840
841        if (!VTABLE_isa(INTERP, sub, s_sub)
842        &&  !VTABLE_isa(INTERP, sub, s_multisub))
843            Parrot_ex_throw_from_c_args(INTERP, NULL,
844                EXCEPTION_INVALID_OPERATION,
845                "Invalid type %d in add_sub()", sub->vtable->base_type);
846
847        VTABLE_set_pmc_keyed_str(INTERP, SELF, name, sub);
848    }
849
850/*
851
852=item C<METHOD add_var(STRING *name, PMC *var)>
853
854Stores the given variable under this namespace, with the given name.
855
856=cut
857
858*/
859
860    METHOD add_var(STRING *name, PMC *var) :manual_wb {
861        VTABLE_set_pmc_keyed_str(INTERP, SELF, name, var);
862    }
863
864/*
865
866=item C<METHOD get_name()>
867
868Returns the name of the namespace as an array of strings.
869
870  $P2 = $P3.'get_name'()
871  $S0 = join '::', $P2            # '::Foo::Bar'
872
873=cut
874
875*/
876
877    METHOD get_name() :no_wb {
878        PMC * const ar = Parrot_pmc_new(INTERP, enum_class_ResizableStringArray);
879        INTVAL elements = 0;
880        PMC *ns        = SELF;
881
882        while (ns) {
883            Parrot_NameSpace_attributes *nsinfo = PARROT_NAMESPACE(ns);
884            VTABLE_unshift_string(INTERP, ar, nsinfo->name);
885            ns = nsinfo->parent;
886            ++elements;
887        }
888
889        /* remove the NULL string of the namespace root */
890        if (elements > 0)
891            VTABLE_shift_string(INTERP, ar);
892
893        RETURN(PMC *ar);
894    }
895/*
896
897=item C<METHOD find_namespace(STRING *name)>
898
899Return the namespace with the given name.
900
901=cut
902
903*/
904
905    METHOD find_namespace(STRING *key) :no_wb {
906        STRING * const s_ns = CONST_STRING(INTERP, "NameSpace");
907        PMC    * const ns   = (PMC *)Parrot_hash_get(INTERP, (Hash *)SELF.get_pointer(),
908                                                     key);
909
910        if (!ns)
911            RETURN(PMC *PMCNULL);
912
913        /* it's a NameSpace */
914        if (VTABLE_isa(INTERP, ns, s_ns))
915            RETURN(PMC *ns);
916
917        RETURN(PMC *PMCNULL);
918    }
919
920/*
921
922=item C<METHOD find_sub(STRING *name)>
923
924Return the Sub PMC with the given name.
925
926=cut
927
928*/
929
930    METHOD find_sub(STRING *key) :no_wb {
931        STRING * const s_sub = CONST_STRING(INTERP, "Sub");
932        PMC    * const sub   = (PMC *)Parrot_hash_get(INTERP, (Hash *)SELF.get_pointer(),
933                                                      key);
934
935        if (!sub)
936            RETURN(PMC *PMCNULL);
937
938        /* it's a Sub */
939        if (VTABLE_isa(INTERP, sub, s_sub))
940            RETURN(PMC *sub);
941
942        RETURN(PMC *PMCNULL);
943    }
944
945/*
946
947=item C<METHOD find_var(STRING *name)>
948
949Return the PMC with the given name.
950
951=cut
952
953*/
954
955    METHOD find_var(STRING *key) :no_wb {
956        PMC * const val = (PMC *)Parrot_hash_get(INTERP, (Hash *)SELF.get_pointer(), key);
957
958        if (!val)
959            RETURN(PMC *PMCNULL);
960
961        RETURN(PMC *val);
962    }
963
964/*
965
966=item C<METHOD del_namespace(STRING *name)>
967
968Deletes the contained NameSpace PMC with the given name.  Throws an invalid
969type exception if the item to delete is not a NameSpace PMC or subclass, and
970does not delete the PMC.
971
972=cut
973
974*/
975
976    METHOD del_namespace(STRING *name) {
977        Hash   * const hash = (Hash *)SELF.get_pointer();
978        PMC    * const ns   = (PMC *)Parrot_hash_get(INTERP, hash, name);
979        STRING * const s_ns = CONST_STRING(INTERP, "NameSpace");
980
981        if (PMC_IS_NULL(ns))
982            RETURN(void);
983
984        if (!VTABLE_isa(INTERP, ns, s_ns))
985            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
986                "Invalid type %d for '%Ss' in del_namespace()",
987                ns->vtable->base_type, name);
988
989        Parrot_hash_delete(INTERP, hash, name);
990    }
991
992/*
993
994=item C<METHOD del_sub(STRING *name)>
995
996Deletes the contained Sub PMC with the given name.  Throws an invalid type
997exception if the item to delete is not a Sub PMC or subclass, and does not
998delete the PMC.
999
1000=cut
1001
1002*/
1003
1004    METHOD del_sub(STRING *name) {
1005        Hash   * const hash  = (Hash *)SELF.get_pointer();
1006        PMC    * const sub   = (PMC *)Parrot_hash_get(INTERP, hash, name);
1007        STRING * const s_sub = CONST_STRING(INTERP, "Sub");
1008
1009        if (PMC_IS_NULL(sub))
1010            RETURN(void);
1011
1012        if (!VTABLE_isa(INTERP, sub, s_sub))
1013            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
1014                "Invalid type %d for '%Ss' in del_sub()",
1015                sub->vtable->base_type, name);
1016
1017        Parrot_hash_delete(INTERP, hash, name);
1018    }
1019
1020/*
1021
1022=item C<METHOD del_var(STRING *name)>
1023
1024Deletes the contained variable-like PMC with the given name.
1025
1026=cut
1027
1028*/
1029
1030    METHOD del_var(STRING *name) {
1031        Parrot_hash_delete(INTERP, (Hash *)SELF.get_pointer(), name);
1032    }
1033
1034/*
1035
1036=item C<METHOD get_sym(STRING *name)>
1037
1038Return the symbol (var or sub) with the given name. This can be used
1039to retrieve symbols, if a NameSpace with the same name exists.
1040
1041=cut
1042
1043*/
1044
1045    METHOD get_sym(STRING *key) :no_wb {
1046        PMC *ns = (PMC *)Parrot_hash_get(INTERP, (Hash *)SELF.get_pointer(), key);
1047
1048        if (!ns)
1049            RETURN(PMC *PMCNULL);
1050
1051        /* it's a NameSpace */
1052        if (ns->vtable == SELF->vtable)
1053            RETURN(PMC *PMCNULL);
1054
1055        if ((PObj_get_FLAGS(ns) & FPA_is_ns_ext)
1056        &&  ns->vtable->base_type == enum_class_FixedPMCArray)
1057            ns = VTABLE_get_pmc_keyed_int(INTERP, ns, NS_slot_var_sub);
1058
1059        RETURN(PMC *ns);
1060    }
1061
1062/*
1063
1064=item C<METHOD export_to(PMC *dest, PMC *what)>
1065
1066Export items from this NameSpace into the C<dest> NameSpace. The items to
1067export are named in C<what>, which may be an array of strings, a hash, or null.
1068If C<what> is an array of strings, interpretation of items in an array follows
1069the conventions of the source (exporting) namespace.
1070If C<what> is a hash, the keys correspond to the names in the source namespace,
1071and the values correspond to the names in the destination namespace.
1072if a hash value is null or an empty string, the name in the hash key is used.
1073A null C<what> requests the 'default' set of items.
1074Any other type passed in C<what> throws an exception.
1075
1076NOTE: exporting 'default' set of items is not yet implemented.
1077
1078=cut
1079
1080*/
1081
1082    METHOD export_to(PMC *dest, PMC *what) :no_wb {
1083        STRING * const s_hash  = CONST_STRING(INTERP, "hash");
1084        STRING * const s_array = CONST_STRING(INTERP, "array");
1085
1086        if (PMC_IS_NULL(dest))
1087            Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_INVALID_OPERATION,
1088                "destination namespace not specified");
1089
1090        if (PMC_IS_NULL(what) || !VTABLE_elements(INTERP, what))
1091            Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_UNIMPLEMENTED,
1092                "exporting default object set not yet implemented");
1093
1094        /* if "what" does "hash", we extract string key/value pairs,
1095         * lookup the object with the name specified in the key,
1096         * and export it with the name specified as value. */
1097        if (VTABLE_does(INTERP, what, s_hash)) {
1098            PMC * const  iter = VTABLE_get_iter(INTERP, what);
1099            const INTVAL n    = VTABLE_elements(INTERP, what);
1100            INTVAL       i;
1101
1102            for (i = 0; i < n; ++i) {
1103                STRING *dest_name;
1104                PMC    *object;
1105                STRING * const src_name = VTABLE_shift_string(INTERP, iter);
1106
1107                if (STRING_IS_NULL(src_name) || STRING_IS_EMPTY(src_name))
1108                    Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_INVALID_OPERATION,
1109                        "source object name not specified");
1110
1111                if (PMC_IS_NULL(VTABLE_get_pmc_keyed_str(INTERP, what, src_name))) {
1112                    dest_name = src_name;
1113                }
1114                else {
1115                    dest_name = VTABLE_get_string_keyed_str(INTERP, what, src_name);
1116                    if (STRING_IS_NULL(dest_name) || STRING_IS_EMPTY(dest_name))
1117                        dest_name = src_name;
1118                }
1119
1120                object = VTABLE_get_pmc_keyed_str(INTERP, SELF, src_name);
1121
1122                if (PMC_IS_NULL(object))
1123                    Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_GLOBAL_NOT_FOUND,
1124                        "object '%Ss' not found in current namespace", src_name);
1125
1126                VTABLE_set_pmc_keyed_str(INTERP, dest, dest_name, object);
1127            }
1128        }
1129        else if (VTABLE_does(INTERP, what, s_array)) {
1130            const INTVAL n = VTABLE_elements(INTERP, what);
1131            INTVAL       i;
1132
1133            for (i = 0; i < n; ++i) {
1134                PMC    *object;
1135                STRING * const name = VTABLE_get_string_keyed_int(INTERP, what, i);
1136
1137                if (STRING_IS_NULL(name) || STRING_IS_EMPTY(name))
1138                    Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_INVALID_OPERATION,
1139                        "object name not specified");
1140
1141                object = VTABLE_get_pmc_keyed_str(INTERP, SELF, name);
1142
1143                if (PMC_IS_NULL(object))
1144                    Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_GLOBAL_NOT_FOUND,
1145                        "object '%Ss' not found in current namespace", name);
1146
1147                VTABLE_set_pmc_keyed_str(INTERP, dest, name, object);
1148            }
1149        }
1150        else
1151            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
1152                "can't handle argument of type %Ss", what->vtable->whoami);
1153    }
1154
1155/*
1156
1157=item C<METHOD get_parent()>
1158
1159Return the parent NameSpace or PMCNULL, if none.
1160
1161=cut
1162
1163*/
1164
1165    METHOD get_parent() :no_wb {
1166        PMC * const parent = PARROT_NAMESPACE(SELF)->parent ?
1167                                 PARROT_NAMESPACE(SELF)->parent : PMCNULL;
1168        RETURN(PMC *parent);
1169    }
1170
1171/*
1172
1173=item C<METHOD get_class()>
1174
1175Returns the class or role PMC that is associated with this namespace.
1176
1177=cut
1178
1179*/
1180
1181    METHOD get_class() :no_wb {
1182        const Parrot_NameSpace_attributes * const nsinfo = PARROT_NAMESPACE(SELF);
1183        PMC              *ret_class     = nsinfo->_class;
1184
1185        if (PMC_IS_NULL(ret_class))
1186            ret_class = PMCNULL;
1187
1188        RETURN(PMC *ret_class);
1189    }
1190
1191/*
1192
1193=item C<METHOD set_class(PMC *class_or_role)>
1194
1195Sets the class or role PMC that is associated with this namespace.
1196
1197=cut
1198
1199*/
1200
1201    METHOD set_class(PMC *class_or_role) {
1202        PARROT_ASSERT_INTERP(class_or_role, INTERP);
1203        PARROT_NAMESPACE(SELF)->_class = class_or_role;
1204    }
1205
1206/*
1207
1208=item C<METHOD get_associated_methods()>
1209
1210Gets the Hash of methods associated with this namespace and removes it from
1211the namespace.
1212
1213=cut
1214
1215*/
1216
1217    METHOD get_associated_methods() :no_wb {
1218        Parrot_NameSpace_attributes * const nsinfo  = PARROT_NAMESPACE(SELF);
1219        PMC              * const ret_methods = nsinfo->methods;
1220
1221        nsinfo->methods = PMCNULL;
1222
1223        RETURN(PMC *ret_methods);
1224    }
1225
1226/*
1227
1228=item C<METHOD get_associated_vtable_methods()>
1229
1230Gets the Hash of vtables associated with this namespace and removes it
1231from the namespace.
1232
1233=cut
1234
1235*/
1236
1237    METHOD get_associated_vtable_methods() :no_wb {
1238        Parrot_NameSpace_attributes * const nsinfo      = PARROT_NAMESPACE(SELF);
1239        PMC              * const ret_methods = nsinfo->vtable;
1240
1241        nsinfo->vtable = PMCNULL;
1242
1243        RETURN(PMC *ret_methods);
1244    }
1245
1246}
1247
1248/*
1249
1250=back
1251
1252=head1 SEE ALSO
1253
1254F<docs/pdds/pdd21_namespaces.pod>
1255
1256=cut
1257
1258*/
1259
1260/*
1261 * Local variables:
1262 *   c-file-style: "parrot"
1263 * End:
1264 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
1265 */
1266