2 Copyright (C) 2003-2010, Parrot Foundation.
7 src/multidispatch.c - Multimethod dispatch for binary opcode functions
11 This system is set up to handle type-based dispatching for binary (two
12 argument) functions. This includes, though isn't necessarily limited to, binary
13 operators such as addition or subtraction.
17 The MMD system is straightforward, and currently must be explicitly invoked,
18 for example by a vtable function. (We reserve the right to use MMD in all
19 circumstances, but currently do not).
23 For the purposes of the API, each MMD-able function is assigned a unique
24 number which is used to find the correct function table. This is the
25 C<func_num> parameter in the following functions. While Parrot isn't
26 restricted to a predefined set of functions, it I<does> set things up so
27 that all the binary vtable functions have a MMD table preinstalled for
28 them, with default behaviour.
40 #include "parrot/compiler.h"
41 #include "parrot/parrot.h"
42 #include "parrot/multidispatch.h"
43 #include "parrot/oplib/ops.h"
44 #include "multidispatch.str"
45 #include "pmc/pmc_nci.h"
46 #include "pmc/pmc_sub.h"
47 #include "pmc/pmc_callcontext.h"
49 /* HEADERIZER HFILE: include/parrot/multidispatch.h */
51 /* HEADERIZER BEGIN: static */
52 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
54 static void mmd_add_multi_global(PARROT_INTERP
,
55 ARGIN(STRING
*sub_name
),
57 __attribute__nonnull__(1)
58 __attribute__nonnull__(2)
59 __attribute__nonnull__(3);
61 static void mmd_add_multi_to_namespace(PARROT_INTERP
,
62 ARGIN(STRING
*ns_name
),
63 ARGIN(STRING
*sub_name
),
65 __attribute__nonnull__(1)
66 __attribute__nonnull__(2)
67 __attribute__nonnull__(3)
68 __attribute__nonnull__(4);
70 PARROT_CANNOT_RETURN_NULL
71 PARROT_WARN_UNUSED_RESULT
72 static PMC
* mmd_build_type_tuple_from_long_sig(PARROT_INTERP
,
73 ARGIN(STRING
*long_sig
))
74 __attribute__nonnull__(1)
75 __attribute__nonnull__(2);
77 PARROT_CANNOT_RETURN_NULL
78 PARROT_WARN_UNUSED_RESULT
79 static PMC
* mmd_build_type_tuple_from_type_list(PARROT_INTERP
,
80 ARGIN(PMC
*type_list
))
81 __attribute__nonnull__(1)
82 __attribute__nonnull__(2);
84 PARROT_WARN_UNUSED_RESULT
85 PARROT_CAN_RETURN_NULL
86 static STRING
* mmd_cache_key_from_types(PARROT_INTERP
,
87 ARGIN(const char *name
),
89 __attribute__nonnull__(1)
90 __attribute__nonnull__(2)
91 __attribute__nonnull__(3);
93 PARROT_WARN_UNUSED_RESULT
94 PARROT_CAN_RETURN_NULL
95 static STRING
* mmd_cache_key_from_values(PARROT_INTERP
,
96 ARGIN(const char *name
),
98 __attribute__nonnull__(1)
99 __attribute__nonnull__(2)
100 __attribute__nonnull__(3);
102 PARROT_WARN_UNUSED_RESULT
103 PARROT_CAN_RETURN_NULL
104 static PMC
* mmd_cvt_to_types(PARROT_INTERP
, ARGIN(PMC
*multi_sig
))
105 __attribute__nonnull__(1)
106 __attribute__nonnull__(2);
108 static UINTVAL
mmd_distance(PARROT_INTERP
,
110 ARGIN(PMC
*arg_tuple
))
111 __attribute__nonnull__(1)
112 __attribute__nonnull__(2)
113 __attribute__nonnull__(3);
115 static void mmd_search_by_sig_obj(PARROT_INTERP
,
118 ARGIN(PMC
*candidates
))
119 __attribute__nonnull__(1)
120 __attribute__nonnull__(2)
121 __attribute__nonnull__(3)
122 __attribute__nonnull__(4);
124 static void mmd_search_global(PARROT_INTERP
,
127 __attribute__nonnull__(1)
128 __attribute__nonnull__(2)
129 __attribute__nonnull__(3);
131 PARROT_WARN_UNUSED_RESULT
132 PARROT_CAN_RETURN_NULL
133 static PMC
* Parrot_mmd_get_cached_multi_sig(PARROT_INTERP
,
135 __attribute__nonnull__(1)
136 __attribute__nonnull__(2);
138 static int Parrot_mmd_maybe_candidate(PARROT_INTERP
,
141 __attribute__nonnull__(1)
142 __attribute__nonnull__(2)
143 __attribute__nonnull__(3);
145 PARROT_CANNOT_RETURN_NULL
146 static PMC
* Parrot_mmd_sort_candidates(PARROT_INTERP
,
147 ARGIN(PMC
*arg_tuple
),
149 __attribute__nonnull__(1)
150 __attribute__nonnull__(2)
151 __attribute__nonnull__(3);
153 #define ASSERT_ARGS_mmd_add_multi_global __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
154 PARROT_ASSERT_ARG(interp) \
155 , PARROT_ASSERT_ARG(sub_name) \
156 , PARROT_ASSERT_ARG(sub_obj))
157 #define ASSERT_ARGS_mmd_add_multi_to_namespace __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
158 PARROT_ASSERT_ARG(interp) \
159 , PARROT_ASSERT_ARG(ns_name) \
160 , PARROT_ASSERT_ARG(sub_name) \
161 , PARROT_ASSERT_ARG(sub_obj))
162 #define ASSERT_ARGS_mmd_build_type_tuple_from_long_sig \
163 __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
164 PARROT_ASSERT_ARG(interp) \
165 , PARROT_ASSERT_ARG(long_sig))
166 #define ASSERT_ARGS_mmd_build_type_tuple_from_type_list \
167 __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
168 PARROT_ASSERT_ARG(interp) \
169 , PARROT_ASSERT_ARG(type_list))
170 #define ASSERT_ARGS_mmd_cache_key_from_types __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
171 PARROT_ASSERT_ARG(interp) \
172 , PARROT_ASSERT_ARG(name) \
173 , PARROT_ASSERT_ARG(types))
174 #define ASSERT_ARGS_mmd_cache_key_from_values __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
175 PARROT_ASSERT_ARG(interp) \
176 , PARROT_ASSERT_ARG(name) \
177 , PARROT_ASSERT_ARG(values))
178 #define ASSERT_ARGS_mmd_cvt_to_types __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
179 PARROT_ASSERT_ARG(interp) \
180 , PARROT_ASSERT_ARG(multi_sig))
181 #define ASSERT_ARGS_mmd_distance __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
182 PARROT_ASSERT_ARG(interp) \
183 , PARROT_ASSERT_ARG(pmc) \
184 , PARROT_ASSERT_ARG(arg_tuple))
185 #define ASSERT_ARGS_mmd_search_by_sig_obj __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
186 PARROT_ASSERT_ARG(interp) \
187 , PARROT_ASSERT_ARG(name) \
188 , PARROT_ASSERT_ARG(sig_obj) \
189 , PARROT_ASSERT_ARG(candidates))
190 #define ASSERT_ARGS_mmd_search_global __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
191 PARROT_ASSERT_ARG(interp) \
192 , PARROT_ASSERT_ARG(name) \
193 , PARROT_ASSERT_ARG(cl))
194 #define ASSERT_ARGS_Parrot_mmd_get_cached_multi_sig \
195 __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
196 PARROT_ASSERT_ARG(interp) \
197 , PARROT_ASSERT_ARG(sub_pmc))
198 #define ASSERT_ARGS_Parrot_mmd_maybe_candidate __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
199 PARROT_ASSERT_ARG(interp) \
200 , PARROT_ASSERT_ARG(pmc) \
201 , PARROT_ASSERT_ARG(cl))
202 #define ASSERT_ARGS_Parrot_mmd_sort_candidates __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
203 PARROT_ASSERT_ARG(interp) \
204 , PARROT_ASSERT_ARG(arg_tuple) \
205 , PARROT_ASSERT_ARG(cl))
206 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
207 /* HEADERIZER END: static */
215 =item C<PMC* Parrot_mmd_find_multi_from_sig_obj(PARROT_INTERP, STRING *name, PMC
218 Collect a list of possible candidates for a given sub name and call signature.
219 Rank the possible candidates by Manhattan Distance, and return the best
220 matching candidate. The candidate list is cached in the CallSignature object,
221 to allow for iterating through it.
223 Currently this only looks in the global "MULTI" namespace.
230 PARROT_WARN_UNUSED_RESULT
231 PARROT_CANNOT_RETURN_NULL
233 Parrot_mmd_find_multi_from_sig_obj(PARROT_INTERP
, ARGIN(STRING
*name
), ARGIN(PMC
*invoke_sig
))
235 ASSERT_ARGS(Parrot_mmd_find_multi_from_sig_obj
)
236 PMC
* const candidate_list
= Parrot_pmc_new(interp
, enum_class_ResizablePMCArray
);
238 mmd_search_by_sig_obj(interp
, name
, invoke_sig
, candidate_list
);
239 mmd_search_global(interp
, name
, candidate_list
);
241 return Parrot_mmd_sort_manhattan_by_sig_pmc(interp
, candidate_list
, invoke_sig
);
246 =item C<void Parrot_mmd_multi_dispatch_from_c_args(PARROT_INTERP, const char
247 *name, const char *sig, ...)>
249 Dispatches to a MultiSub from a variable-sized list of C arguments. The
250 multiple dispatch system will figure out which sub should be called based on
251 the types of the arguments passed in.
253 Return arguments must be passed as a reference to the PMC, string, number, or
254 integer, so the result can be set.
261 PARROT_CAN_RETURN_NULL
263 Parrot_mmd_multi_dispatch_from_c_args(PARROT_INTERP
,
264 ARGIN(const char *name
), ARGIN(const char *sig
), ...)
266 ASSERT_ARGS(Parrot_mmd_multi_dispatch_from_c_args
)
269 const char *arg_sig
, *ret_sig
;
271 Parrot_pcc_split_signature_string(sig
, &arg_sig
, &ret_sig
);
274 call_obj
= Parrot_pcc_build_call_from_varargs(interp
, PMCNULL
, arg_sig
, &args
);
276 /* Check the cache. */
277 sub
= Parrot_mmd_cache_lookup_by_types(interp
, interp
->op_mmd_cache
, name
,
278 VTABLE_get_pmc(interp
, call_obj
));
280 if (PMC_IS_NULL(sub
)) {
281 sub
= Parrot_mmd_find_multi_from_sig_obj(interp
,
282 Parrot_str_new_constant(interp
, name
), call_obj
);
284 if (!PMC_IS_NULL(sub
))
285 Parrot_mmd_cache_store_by_types(interp
, interp
->op_mmd_cache
, name
,
286 VTABLE_get_pmc(interp
, call_obj
), sub
);
289 if (PMC_IS_NULL(sub
))
290 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_METHOD_NOT_FOUND
,
291 "Multiple Dispatch: No suitable candidate found for '%s',"
292 " with signature '%s'", name
, sig
);
295 Parrot_io_eprintf(interp
, "candidate found for '%s', with signature '%s'\n",
297 Parrot_io_eprintf(interp
, "type of candidate found: %Ss\n",
298 VTABLE_name(interp
, sub
));
301 Parrot_pcc_invoke_from_sig_object(interp
, sub
, call_obj
);
302 call_obj
= Parrot_pcc_get_signature(interp
, CURRENT_CONTEXT(interp
));
303 Parrot_pcc_fill_params_from_varargs(interp
, call_obj
, ret_sig
, &args
,
304 PARROT_ERRORS_RESULT_COUNT_FLAG
);
311 =item C<PMC * Parrot_mmd_find_multi_from_long_sig(PARROT_INTERP, STRING *name,
314 Find the best candidate multi for a given sub name and signature. The signature
315 is a string containing a comma-delimited list of type names.
317 Currently only searches the global MULTI namespace.
324 PARROT_CAN_RETURN_NULL
325 PARROT_WARN_UNUSED_RESULT
327 Parrot_mmd_find_multi_from_long_sig(PARROT_INTERP
, ARGIN(STRING
*name
),
328 ARGIN(STRING
*long_sig
))
330 ASSERT_ARGS(Parrot_mmd_find_multi_from_long_sig
)
331 STRING
* const multi_str
= CONST_STRING(interp
, "MULTI");
332 PMC
* const ns
= Parrot_make_namespace_keyed_str(interp
,
333 interp
->root_namespace
, multi_str
);
334 PMC
* const multi_sub
= Parrot_get_global(interp
, ns
, name
);
336 if (PMC_IS_NULL(multi_sub
)) {
340 PMC
* const type_tuple
= mmd_build_type_tuple_from_long_sig(interp
, long_sig
);
341 return Parrot_mmd_sort_candidates(interp
, type_tuple
, multi_sub
);
348 =item C<PMC * Parrot_mmd_sort_manhattan_by_sig_pmc(PARROT_INTERP, PMC
349 *candidates, PMC *invoke_sig)>
351 Given an array PMC (usually a MultiSub) and a CallSignature PMC, sorts the mmd
352 candidates by their manhattan distance to the signature args and returns the
360 PARROT_CAN_RETURN_NULL
361 PARROT_WARN_UNUSED_RESULT
363 Parrot_mmd_sort_manhattan_by_sig_pmc(PARROT_INTERP
, ARGIN(PMC
*candidates
),
364 ARGIN(PMC
*invoke_sig
))
366 ASSERT_ARGS(Parrot_mmd_sort_manhattan_by_sig_pmc
)
367 const INTVAL n
= VTABLE_elements(interp
, candidates
);
372 return Parrot_mmd_sort_candidates(interp
,
373 VTABLE_get_pmc(interp
, invoke_sig
), candidates
);
378 =item C<static PMC* mmd_build_type_tuple_from_type_list(PARROT_INTERP, PMC
381 Construct a FixedIntegerArray of type numbers from an array of
382 type names. Used for multiple dispatch.
388 PARROT_CANNOT_RETURN_NULL
389 PARROT_WARN_UNUSED_RESULT
391 mmd_build_type_tuple_from_type_list(PARROT_INTERP
, ARGIN(PMC
*type_list
))
393 ASSERT_ARGS(mmd_build_type_tuple_from_type_list
)
394 INTVAL param_count
= VTABLE_elements(interp
, type_list
);
395 PMC
*multi_sig
= Parrot_pmc_new_constant_init_int(interp
,
396 enum_class_FixedIntegerArray
, param_count
);
399 for (i
= 0; i
< param_count
; ++i
) {
400 STRING
*type_name
= VTABLE_get_string_keyed_int(interp
, type_list
, i
);
403 if (Parrot_str_equal(interp
, type_name
, CONST_STRING(interp
, "DEFAULT")))
404 type
= enum_type_PMC
;
405 else if (Parrot_str_equal(interp
, type_name
, CONST_STRING(interp
, "STRING")))
406 type
= enum_type_STRING
;
407 else if (Parrot_str_equal(interp
, type_name
, CONST_STRING(interp
, "INTVAL")))
408 type
= enum_type_INTVAL
;
409 else if (Parrot_str_equal(interp
, type_name
, CONST_STRING(interp
, "FLOATVAL")))
410 type
= enum_type_FLOATVAL
;
412 type
= Parrot_pmc_get_type_str(interp
, type_name
);
414 VTABLE_set_integer_keyed_int(interp
, multi_sig
, i
, type
);
423 =item C<static PMC* mmd_build_type_tuple_from_long_sig(PARROT_INTERP, STRING
426 Construct a FixedIntegerArray of type numbers from a comma-delimited string of
427 type names. Used for multiple dispatch.
433 PARROT_CANNOT_RETURN_NULL
434 PARROT_WARN_UNUSED_RESULT
436 mmd_build_type_tuple_from_long_sig(PARROT_INTERP
, ARGIN(STRING
*long_sig
))
438 ASSERT_ARGS(mmd_build_type_tuple_from_long_sig
)
439 PMC
*type_list
= Parrot_str_split(interp
, CONST_STRING(interp
, ","), long_sig
);
441 return mmd_build_type_tuple_from_type_list(interp
, type_list
);
447 =item C<PMC* Parrot_mmd_build_type_tuple_from_sig_obj(PARROT_INTERP, PMC
450 Construct a FixedIntegerArray of type numbers from the arguments of a Call
451 Signature object. Used for multiple dispatch.
458 PARROT_CANNOT_RETURN_NULL
459 PARROT_WARN_UNUSED_RESULT
461 Parrot_mmd_build_type_tuple_from_sig_obj(PARROT_INTERP
, ARGIN(PMC
*sig_obj
))
463 ASSERT_ARGS(Parrot_mmd_build_type_tuple_from_sig_obj
)
464 return VTABLE_get_pmc(interp
, sig_obj
);
470 =item C<static PMC* mmd_cvt_to_types(PARROT_INTERP, PMC *multi_sig)>
472 Given a ResizablePMCArray PMC containing some form of type identifier (either
473 the string name of a class or a PMC representing the type), resolves all type
474 references to type IDs, if possible. If that's not possible, returns PMCNULL.
475 In that case you can't dispatch to the multi variant with this type signature,
476 as Parrot doesn't yet know about the respective types requested -- you have to
479 Otherwise, returns a ResizableIntegerArray PMC full of type IDs representing
480 the signature of a multi variant to which you may be able to dispatch.
488 PARROT_WARN_UNUSED_RESULT
489 PARROT_CAN_RETURN_NULL
491 mmd_cvt_to_types(PARROT_INTERP
, ARGIN(PMC
*multi_sig
))
493 ASSERT_ARGS(mmd_cvt_to_types
)
495 const INTVAL n
= VTABLE_elements(interp
, multi_sig
);
498 for (i
= 0; i
< n
; ++i
) {
499 PMC
* const sig_elem
= VTABLE_get_pmc_keyed_int(interp
, multi_sig
, i
);
502 if (sig_elem
->vtable
->base_type
== enum_class_String
) {
503 STRING
* const sig
= VTABLE_get_string(interp
, sig_elem
);
508 type
= Parrot_pmc_get_type_str(interp
, sig
);
510 if (type
== enum_type_undef
)
513 else if (sig_elem
->vtable
->base_type
== enum_class_Integer
) {
514 type
= VTABLE_get_integer(interp
, sig_elem
);
517 type
= Parrot_pmc_get_type(interp
, sig_elem
);
519 /* create destination PMC only as necessary */
521 ar
= Parrot_pmc_new_init_int(interp
, enum_class_FixedIntegerArray
, n
);
523 VTABLE_set_integer_keyed_int(interp
, ar
, i
, type
);
532 =item C<static PMC * Parrot_mmd_get_cached_multi_sig(PARROT_INTERP, PMC
535 Get the cached multisig of the given sub, if one exists. The cached signature
536 might be in different formats, so put it into a type tuple like the rest of the
543 PARROT_WARN_UNUSED_RESULT
544 PARROT_CAN_RETURN_NULL
546 Parrot_mmd_get_cached_multi_sig(PARROT_INTERP
, ARGIN(PMC
*sub_pmc
))
548 ASSERT_ARGS(Parrot_mmd_get_cached_multi_sig
)
549 if (VTABLE_isa(interp
, sub_pmc
, CONST_STRING(interp
, "Sub"))) {
550 Parrot_Sub_attributes
*sub
;
553 PMC_get_sub(interp
, sub_pmc
, sub
);
554 multi_sig
= sub
->multi_signature
;
556 if (multi_sig
->vtable
->base_type
== enum_class_FixedPMCArray
) {
557 PMC
*converted_sig
= mmd_cvt_to_types(interp
, multi_sig
);
559 if (PMC_IS_NULL(converted_sig
))
562 multi_sig
= sub
->multi_signature
= converted_sig
;
572 #define MMD_BIG_DISTANCE 0x7fff
576 =item C<static UINTVAL mmd_distance(PARROT_INTERP, PMC *pmc, PMC *arg_tuple)>
578 Create Manhattan Distance of sub C<pmc> against given argument types.
579 0xffff is the maximum distance
586 mmd_distance(PARROT_INTERP
, ARGIN(PMC
*pmc
), ARGIN(PMC
*arg_tuple
))
588 ASSERT_ARGS(mmd_distance
)
589 PMC
*multi_sig
, *mro
;
590 Parrot_Sub_attributes
*sub
;
591 INTVAL args
, dist
, i
, j
, n
, m
;
593 /* has to be a builtin multi method */
594 if (pmc
->vtable
->base_type
== enum_class_NCI
) {
595 GETATTR_NCI_multi_sig(interp
, pmc
, multi_sig
);
596 if (PMC_IS_NULL(multi_sig
)) {
599 GETATTR_NCI_long_signature(interp
, pmc
, long_sig
);
600 multi_sig
= mmd_build_type_tuple_from_long_sig(interp
, long_sig
);
601 SETATTR_NCI_multi_sig(interp
, pmc
, multi_sig
);
605 /* not a multi; no distance */
606 PMC_get_sub(interp
, pmc
, sub
);
607 if (!sub
->multi_signature
)
610 multi_sig
= Parrot_mmd_get_cached_multi_sig(interp
, pmc
);
613 if (PMC_IS_NULL(multi_sig
))
614 return MMD_BIG_DISTANCE
;
616 n
= VTABLE_elements(interp
, multi_sig
);
617 args
= VTABLE_elements(interp
, arg_tuple
);
620 * arg_tuple may have more arguments - only the
621 * n multi_sig invocants are counted
624 return MMD_BIG_DISTANCE
;
629 dist
= PARROT_MMD_MAX_CLASS_DEPTH
;
631 /* now go through args */
632 for (i
= 0; i
< n
; ++i
) {
633 const INTVAL type_sig
= VTABLE_get_integer_keyed_int(interp
, multi_sig
, i
);
634 INTVAL type_call
= VTABLE_get_integer_keyed_int(interp
, arg_tuple
, i
);
635 if (type_sig
== type_call
)
638 /* promote primitives to their PMC equivalents, as PCC will autobox
639 * them. If it's a direct autobox, int->Integer, str->String, or
640 * num->Num, the distance is 1 and we move to the next arg. If it's
641 * autoboxing to "any" PMC type, we increment the distance and continue
642 * weighing other things. A direct autobox should be cheaper than an
643 * autobox plus type conversion or implicit type acceptance. */
645 case enum_type_INTVAL
:
646 if (type_sig
== enum_class_Integer
) { dist
++; continue; }
647 if (type_sig
== enum_type_PMC
||
648 (type_sig
>= enum_class_default
&& type_sig
< enum_class_core_max
)) {
650 type_call
= enum_class_Integer
;
653 case enum_type_FLOATVAL
:
654 if (type_sig
== enum_class_Float
) { dist
++; continue; }
655 if (type_sig
== enum_type_PMC
||
656 (type_sig
>= enum_class_default
&& type_sig
< enum_class_core_max
)) {
658 type_call
= enum_class_Float
;
661 case enum_type_STRING
:
662 if (type_sig
== enum_class_String
) { dist
++; continue; }
663 if (type_sig
== enum_type_PMC
||
664 (type_sig
>= enum_class_default
&& type_sig
< enum_class_core_max
)) {
666 type_call
= enum_class_String
;
674 * different native types are very different, except a PMC
675 * which matches any PMC
677 if (type_call
<= 0 && type_sig
== enum_type_PMC
) {
682 if ((type_sig
<= 0 && type_sig
!= enum_type_PMC
) || type_call
<= 0) {
683 dist
= MMD_BIG_DISTANCE
;
688 * now consider MRO of types the signature type has to be somewhere
689 * in the MRO of the type_call
691 mro
= interp
->vtables
[type_call
]->mro
;
692 m
= VTABLE_elements(interp
, mro
);
694 for (j
= 0; j
< m
; ++j
) {
695 PMC
* const cl
= VTABLE_get_pmc_keyed_int(interp
, mro
, j
);
697 if (cl
->vtable
->base_type
== type_sig
)
699 if (VTABLE_type(interp
, cl
) == type_sig
)
706 * if the type wasn't in MRO check, if any PMC matches
707 * in that case use the distance + 1 (of an any PMC parent)
709 if (j
== m
&& type_sig
!= enum_type_PMC
) {
710 dist
= MMD_BIG_DISTANCE
;
720 s1
= Parrot_get_datatype_name(interp
, type_sig
);
722 s1
= interp
->vtables
[type_sig
]->whoami
;
725 s2
= Parrot_get_datatype_name(interp
, type_call
);
727 s2
= interp
->vtables
[type_call
]->whoami
;
729 Parrot_io_eprintf(interp
, "arg %d: dist %d sig %Ss arg %Ss\n",
741 =item C<static PMC * Parrot_mmd_sort_candidates(PARROT_INTERP, PMC *arg_tuple,
744 Sort the candidate list C<cl> by Manhattan Distance, returning the best
751 PARROT_CANNOT_RETURN_NULL
753 Parrot_mmd_sort_candidates(PARROT_INTERP
, ARGIN(PMC
*arg_tuple
), ARGIN(PMC
*cl
))
755 ASSERT_ARGS(Parrot_mmd_sort_candidates
)
756 PMC
*best_candidate
= PMCNULL
;
757 INTVAL best_distance
= MMD_BIG_DISTANCE
;
758 const INTVAL n
= VTABLE_elements(interp
, cl
);
761 for (i
= 0; i
< n
; ++i
) {
762 PMC
* const pmc
= VTABLE_get_pmc_keyed_int(interp
, cl
, i
);
763 const INTVAL d
= mmd_distance(interp
, pmc
, arg_tuple
);
764 if (d
< best_distance
) {
765 best_candidate
= pmc
;
770 return best_candidate
;
776 =item C<static int Parrot_mmd_maybe_candidate(PARROT_INTERP, PMC *pmc, PMC *cl)>
778 If the candidate C<pmc> is a Sub PMC, push it on the candidate list and
779 return TRUE to stop further search.
781 If the candidate is a MultiSub remember all matching Subs and return FALSE
782 to continue searching outer scopes.
789 Parrot_mmd_maybe_candidate(PARROT_INTERP
, ARGIN(PMC
*pmc
), ARGIN(PMC
*cl
))
791 ASSERT_ARGS(Parrot_mmd_maybe_candidate
)
792 STRING
* const _sub
= CONST_STRING(interp
, "Sub");
793 STRING
* const _multi_sub
= CONST_STRING(interp
, "MultiSub");
797 if (VTABLE_isa(interp
, pmc
, _sub
)) {
798 /* a plain sub stops outer searches */
799 VTABLE_push_pmc(interp
, cl
, pmc
);
803 /* not a Sub or MultiSub - ignore */
804 if (!VTABLE_isa(interp
, pmc
, _multi_sub
))
807 /* ok we have a multi sub pmc, which is an array of candidates */
808 n
= VTABLE_elements(interp
, pmc
);
810 for (i
= 0; i
< n
; ++i
) {
811 PMC
* const multi_sub
= VTABLE_get_pmc_keyed_int(interp
, pmc
, i
);
812 VTABLE_push_pmc(interp
, cl
, multi_sub
);
821 =item C<static void mmd_search_by_sig_obj(PARROT_INTERP, STRING *name, PMC
822 *sig_obj, PMC *candidates)>
824 Search the namespace of the first argument to the sub call for matching
832 mmd_search_by_sig_obj(PARROT_INTERP
, ARGIN(STRING
*name
),
833 ARGIN(PMC
*sig_obj
), ARGIN(PMC
*candidates
))
835 ASSERT_ARGS(mmd_search_by_sig_obj
)
836 PMC
*first_arg
= VTABLE_get_pmc_keyed_int(interp
, sig_obj
, 0);
839 if (PMC_IS_NULL(first_arg
))
842 ns
= VTABLE_get_namespace(interp
, first_arg
);
847 multi_sub
= Parrot_get_global(interp
, ns
, name
);
849 if (PMC_IS_NULL(multi_sub
))
852 Parrot_mmd_maybe_candidate(interp
, multi_sub
, candidates
);
858 =item C<static void mmd_search_global(PARROT_INTERP, STRING *name, PMC *cl)>
860 Search the builtin namespace for matching candidates.
867 mmd_search_global(PARROT_INTERP
, ARGIN(STRING
*name
), ARGIN(PMC
*cl
))
869 ASSERT_ARGS(mmd_search_global
)
870 STRING
* const multi_str
= CONST_STRING(interp
, "MULTI");
871 PMC
* const ns
= Parrot_get_namespace_keyed_str(interp
,
872 interp
->root_namespace
, multi_str
);
873 PMC
*multi_sub
= Parrot_get_global(interp
, ns
, name
);
875 if (PMC_IS_NULL(multi_sub
))
878 Parrot_mmd_maybe_candidate(interp
, multi_sub
, cl
);
884 =item C<static void mmd_add_multi_global(PARROT_INTERP, STRING *sub_name, PMC
887 Create a MultiSub, or add a variant to an existing MultiSub. The MultiSub is
888 stored in the global MULTI namespace.
895 mmd_add_multi_global(PARROT_INTERP
, ARGIN(STRING
*sub_name
), ARGIN(PMC
*sub_obj
))
897 ASSERT_ARGS(mmd_add_multi_global
)
898 STRING
* const multi_str
= CONST_STRING(interp
, "MULTI");
899 PMC
* const ns
= Parrot_make_namespace_keyed_str(interp
,
900 interp
->root_namespace
, multi_str
);
901 PMC
*multi_sub
= Parrot_get_global(interp
, ns
, sub_name
);
903 if (PMC_IS_NULL(multi_sub
)) {
904 multi_sub
= Parrot_pmc_new_constant(interp
, enum_class_MultiSub
);
905 Parrot_set_global(interp
, ns
, sub_name
, multi_sub
);
908 PARROT_ASSERT(multi_sub
->vtable
->base_type
== enum_class_MultiSub
);
909 VTABLE_push_pmc(interp
, multi_sub
, sub_obj
);
915 =item C<static void mmd_add_multi_to_namespace(PARROT_INTERP, STRING *ns_name,
916 STRING *sub_name, PMC *sub_obj)>
918 Create a MultiSub, or add a variant to an existing MultiSub. The MultiSub is
919 added as a method to a class.
926 mmd_add_multi_to_namespace(PARROT_INTERP
, ARGIN(STRING
*ns_name
),
927 ARGIN(STRING
*sub_name
), ARGIN(PMC
*sub_obj
))
929 ASSERT_ARGS(mmd_add_multi_to_namespace
)
930 PMC
* const hll_ns
= VTABLE_get_pmc_keyed_int(interp
,
931 interp
->HLL_namespace
,
932 Parrot_pcc_get_HLL(interp
, CURRENT_CONTEXT(interp
)));
933 PMC
* const ns
= Parrot_make_namespace_keyed_str(interp
, hll_ns
, ns_name
);
934 PMC
*multi_sub
= Parrot_get_global(interp
, ns
, sub_name
);
936 if (PMC_IS_NULL(multi_sub
)) {
937 multi_sub
= Parrot_pmc_new_constant(interp
, enum_class_MultiSub
);
938 Parrot_set_global(interp
, ns
, sub_name
, multi_sub
);
941 PARROT_ASSERT(multi_sub
->vtable
->base_type
== enum_class_MultiSub
);
942 VTABLE_push_pmc(interp
, multi_sub
, sub_obj
);
948 =item C<void Parrot_mmd_add_multi_from_long_sig(PARROT_INTERP, STRING *sub_name,
949 STRING *long_sig, PMC *sub_obj)>
951 Create a MultiSub, or add a variant to an existing MultiSub. The MultiSub is
952 stored in the global MULTI namespace.
960 Parrot_mmd_add_multi_from_long_sig(PARROT_INTERP
,
961 ARGIN(STRING
*sub_name
), ARGIN(STRING
*long_sig
), ARGIN(PMC
*sub_obj
))
963 ASSERT_ARGS(Parrot_mmd_add_multi_from_long_sig
)
964 Parrot_Sub_attributes
*sub
;
965 STRING
*sub_str
= CONST_STRING(interp
, "Sub");
966 STRING
*closure_str
= CONST_STRING(interp
, "Closure");
967 PMC
*type_list
= Parrot_str_split(interp
, CONST_STRING(interp
, ","), long_sig
);
968 STRING
*ns_name
= VTABLE_get_string_keyed_int(interp
, type_list
, 0);
970 /* Attach a type tuple array to the sub for multi dispatch */
971 PMC
*multi_sig
= mmd_build_type_tuple_from_type_list(interp
, type_list
);
973 if (sub_obj
->vtable
->base_type
== enum_class_NCI
) {
974 SETATTR_NCI_multi_sig(interp
, sub_obj
, multi_sig
);
976 else if (VTABLE_isa(interp
, sub_obj
, sub_str
)
977 || VTABLE_isa(interp
, sub_obj
, closure_str
)) {
978 PMC_get_sub(interp
, sub_obj
, sub
);
979 sub
->multi_signature
= multi_sig
;
982 mmd_add_multi_to_namespace(interp
, ns_name
, sub_name
, sub_obj
);
983 mmd_add_multi_global(interp
, sub_name
, sub_obj
);
989 =item C<void Parrot_mmd_add_multi_from_c_args(PARROT_INTERP, const char
990 *sub_name, const char *short_sig, const char *long_sig, funcptr_t
993 Create a MultiSub, or add a variant to an existing MultiSub. The MultiSub is
994 stored in the specified namespace.
1002 Parrot_mmd_add_multi_from_c_args(PARROT_INTERP
,
1003 ARGIN(const char *sub_name
), ARGIN(const char *short_sig
),
1004 ARGIN(const char *long_sig
), ARGIN(funcptr_t multi_func_ptr
))
1006 ASSERT_ARGS(Parrot_mmd_add_multi_from_c_args
)
1007 STRING
*comma
= CONST_STRING(interp
, ",");
1008 STRING
*sub_name_str
= Parrot_str_new_constant(interp
, sub_name
);
1009 STRING
*long_sig_str
= Parrot_str_new_constant(interp
, long_sig
);
1010 STRING
*short_sig_str
= Parrot_str_new_constant(interp
, short_sig
);
1011 PMC
*type_list
= Parrot_str_split(interp
, comma
, long_sig_str
);
1012 STRING
*ns_name
= VTABLE_get_string_keyed_int(interp
, type_list
, 0);
1014 /* Create an NCI sub for the C function */
1015 PMC
*sub_obj
= Parrot_pmc_new_constant(interp
, enum_class_NCI
);
1016 PMC
*multi_sig
= mmd_build_type_tuple_from_long_sig(interp
,
1019 VTABLE_set_pointer_keyed_str(interp
, sub_obj
, short_sig_str
,
1020 F2DPTR(multi_func_ptr
));
1022 /* Attach a type tuple array to the NCI sub for multi dispatch */
1023 SETATTR_NCI_multi_sig(interp
, sub_obj
, multi_sig
);
1025 mmd_add_multi_to_namespace(interp
, ns_name
, sub_name_str
, sub_obj
);
1026 mmd_add_multi_global(interp
, sub_name_str
, sub_obj
);
1031 =item C<void Parrot_mmd_add_multi_list_from_c_args(PARROT_INTERP, const
1032 multi_func_list *mmd_info, INTVAL elements)>
1034 Create a collection of multiple dispatch subs from a C structure of
1035 information. Iterate through the list of details passed in. For each entry
1036 create a MultiSub or add a variant to an existing MultiSub. MultiSubs are
1037 created in the global 'MULTI' namespace in the Parrot HLL.
1039 Typically used to create all the multiple dispatch routines
1040 declared in a PMC from the PMC's class initialization function.
1048 Parrot_mmd_add_multi_list_from_c_args(PARROT_INTERP
,
1049 ARGIN(const multi_func_list
*mmd_info
), INTVAL elements
)
1051 ASSERT_ARGS(Parrot_mmd_add_multi_list_from_c_args
)
1053 for (i
= 0; i
< elements
; ++i
) {
1054 funcptr_t func_ptr
= mmd_info
[i
].func_ptr
;
1056 STRING
*sub_name
= mmd_info
[i
].multi_name
;
1057 STRING
*long_sig
= mmd_info
[i
].full_sig
;
1058 STRING
*short_sig
= mmd_info
[i
].short_sig
;
1059 STRING
*ns_name
= mmd_info
[i
].ns_name
;
1061 /* Create an NCI sub for the C function */
1062 PMC
*sub_obj
= Parrot_pmc_new_constant(interp
, enum_class_NCI
);
1064 VTABLE_set_pointer_keyed_str(interp
, sub_obj
, short_sig
,
1067 /* Attach a type tuple array to the NCI sub for multi dispatch */
1068 SETATTR_NCI_long_signature(interp
, sub_obj
, long_sig
);
1070 mmd_add_multi_to_namespace(interp
, ns_name
, sub_name
, sub_obj
);
1071 mmd_add_multi_global(interp
, sub_name
, sub_obj
);
1078 =item C<MMD_Cache * Parrot_mmd_cache_create(PARROT_INTERP)>
1080 Creates and returns a new MMD cache.
1087 PARROT_CANNOT_RETURN_NULL
1089 Parrot_mmd_cache_create(PARROT_INTERP
)
1091 ASSERT_ARGS(Parrot_mmd_cache_create
)
1093 Hash
*cache
= parrot_new_hash(interp
);
1100 =item C<static STRING * mmd_cache_key_from_values(PARROT_INTERP, const char
1101 *name, PMC *values)>
1103 Generates an MMD cache key from an array of values.
1109 PARROT_WARN_UNUSED_RESULT
1110 PARROT_CAN_RETURN_NULL
1112 mmd_cache_key_from_values(PARROT_INTERP
, ARGIN(const char *name
),
1115 ASSERT_ARGS(mmd_cache_key_from_values
)
1116 /* Build array of type IDs, which we'll then use as a string to key into
1118 const INTVAL num_values
= VTABLE_elements(interp
, values
);
1119 const INTVAL name_len
= name
? strlen(name
) + 1: 0;
1120 const size_t id_size
= num_values
* sizeof (INTVAL
) + name_len
;
1121 INTVAL
*type_ids
= mem_gc_allocate_n_typed(interp
, num_values
+ name_len
, INTVAL
);
1125 for (i
= 0; i
< num_values
; ++i
) {
1126 const INTVAL id
= VTABLE_type(interp
, VTABLE_get_pmc_keyed_int(interp
, values
, i
));
1128 mem_gc_free(interp
, type_ids
);
1136 strcpy((char *)(type_ids
+ num_values
), name
);
1138 key
= Parrot_str_new(interp
, (char *)type_ids
, id_size
);
1139 mem_gc_free(interp
, type_ids
);
1147 =item C<PMC * Parrot_mmd_cache_lookup_by_values(PARROT_INTERP, MMD_Cache *cache,
1148 const char *name, PMC *values)>
1150 Takes an array of values for the call and does a lookup in the MMD cache.
1157 PARROT_WARN_UNUSED_RESULT
1158 PARROT_CAN_RETURN_NULL
1160 Parrot_mmd_cache_lookup_by_values(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
),
1161 ARGIN(const char *name
), ARGIN(PMC
*values
))
1163 ASSERT_ARGS(Parrot_mmd_cache_lookup_by_values
)
1164 STRING
* const key
= mmd_cache_key_from_values(interp
, name
, values
);
1167 return (PMC
*)parrot_hash_get(interp
, cache
, key
);
1175 =item C<void Parrot_mmd_cache_store_by_values(PARROT_INTERP, MMD_Cache *cache,
1176 const char *name, PMC *values, PMC *chosen)>
1178 Takes an array of values for the call along with a chosen candidate and puts
1187 Parrot_mmd_cache_store_by_values(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
),
1188 ARGIN(const char *name
), ARGIN(PMC
*values
), ARGIN(PMC
*chosen
))
1190 ASSERT_ARGS(Parrot_mmd_cache_store_by_values
)
1191 STRING
* const key
= mmd_cache_key_from_values(interp
, name
, values
);
1194 parrot_hash_put(interp
, cache
, key
, chosen
);
1200 =item C<static STRING * mmd_cache_key_from_types(PARROT_INTERP, const char
1203 Generates an MMD cache key from an array of types.
1209 PARROT_WARN_UNUSED_RESULT
1210 PARROT_CAN_RETURN_NULL
1212 mmd_cache_key_from_types(PARROT_INTERP
, ARGIN(const char *name
),
1215 ASSERT_ARGS(mmd_cache_key_from_types
)
1216 /* Build array of type IDs, which we'll then use as a string to key into
1218 const INTVAL num_types
= VTABLE_elements(interp
, types
);
1219 const INTVAL name_len
= name
? strlen(name
) + 1: 0;
1220 const size_t id_size
= num_types
* sizeof (INTVAL
) + name_len
;
1221 INTVAL
* const type_ids
= mem_gc_allocate_n_typed(interp
, num_types
+ name_len
, INTVAL
);
1226 for (i
= 0; i
< num_types
; ++i
) {
1227 const INTVAL id
= VTABLE_get_integer_keyed_int(interp
, types
, i
);
1230 mem_gc_free(interp
, type_ids
);
1238 strcpy((char *)(type_ids
+ num_types
), name
);
1240 key
= Parrot_str_new(interp
, (char *)type_ids
, id_size
);
1242 mem_gc_free(interp
, type_ids
);
1249 =item C<PMC * Parrot_mmd_cache_lookup_by_types(PARROT_INTERP, MMD_Cache *cache,
1250 const char *name, PMC *types)>
1252 Takes an array of types for the call and does a lookup in the MMD cache.
1259 PARROT_WARN_UNUSED_RESULT
1260 PARROT_CAN_RETURN_NULL
1262 Parrot_mmd_cache_lookup_by_types(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
),
1263 ARGIN(const char *name
), ARGIN(PMC
*types
))
1265 ASSERT_ARGS(Parrot_mmd_cache_lookup_by_types
)
1266 const STRING
* const key
= mmd_cache_key_from_types(interp
, name
, types
);
1269 return (PMC
*)parrot_hash_get(interp
, cache
, key
);
1277 =item C<void Parrot_mmd_cache_store_by_types(PARROT_INTERP, MMD_Cache *cache,
1278 const char *name, PMC *types, PMC *chosen)>
1280 Takes an array of types for the call along with a chosen candidate and puts
1281 it into the cache. The name parameter is optional, and if the cache is already
1282 tied to an individual multi can be null.
1290 Parrot_mmd_cache_store_by_types(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
),
1291 ARGIN(const char *name
), ARGIN(PMC
*types
), ARGIN(PMC
*chosen
))
1293 ASSERT_ARGS(Parrot_mmd_cache_store_by_types
)
1294 STRING
* const key
= mmd_cache_key_from_types(interp
, name
, types
);
1297 parrot_hash_put(interp
, cache
, key
, chosen
);
1303 =item C<void Parrot_mmd_cache_mark(PARROT_INTERP, MMD_Cache *cache)>
1305 GC-marks an MMD cache.
1313 Parrot_mmd_cache_mark(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
))
1315 ASSERT_ARGS(Parrot_mmd_cache_mark
)
1316 /* As a small future optimization, note that we only *really* need to mark
1317 * keys - the candidates will be referenced outside the cache, provided it's
1318 * invalidated properly. */
1319 parrot_mark_hash(interp
, cache
);
1325 =item C<void Parrot_mmd_cache_destroy(PARROT_INTERP, MMD_Cache *cache)>
1327 Destroys an MMD cache.
1335 Parrot_mmd_cache_destroy(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
))
1337 ASSERT_ARGS(Parrot_mmd_cache_destroy
)
1338 parrot_hash_destroy(interp
, cache
);
1348 F<include/parrot/multidispatch.h>,
1349 F<http://svn.perl.org/perl6/doc/trunk/design/apo/A12.pod>,
1350 F<http://svn.perl.org/perl6/doc/trunk/design/syn/S12.pod>
1359 * c-file-style: "parrot"
1361 * vim: expandtab shiftwidth=4: