* docs/pmc.pod:
[parrot.git] / src / mmd.c
blob6bec550e51c37404097ba8c666da0547a9861684
1 /*
2 Copyright (C) 2003-2006, The Perl Foundation.
3 $Id$
5 =head1 NAME
7 src/mmd.c - Multimethod dispatch for binary opcode functions
9 =head1 SYNOPSIS
11 This system is set up to handle type-based dispatching for binary (i.e.
12 two-arg) functions. This includes, though isn't necessarily limited to,
13 binary operators such as addition or subtraction.
15 =head1 DESCRIPTION
17 The MMD system is straightforward, and currently must be explicitly
18 invoked, for example by a vtable function. (We may reserve the right to
19 use MMD in all circumstances, but currently do not).
21 =head2 API
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.
30 =head2 Remarks
32 C<< binop_mmd_funcs->x >> and C<< ->y >> are table sizes
33 not highest type in table.
35 =head2 Functions
37 =over 4
39 =cut
43 #include "parrot/parrot.h"
44 #include "parrot/oplib/ops.h"
45 #include "mmd.str"
46 #include <assert.h>
48 #define MMD_DEBUG 0
50 static void mmd_create_builtin_multi_meth_2(Interp *, PMC *ns,
51 INTVAL func_nr, INTVAL type, INTVAL right, funcptr_t func_ptr);
53 #ifndef NDEBUG
54 static void
55 dump_mmd(Interp *interpreter, INTVAL function)
57 UINTVAL x, y;
58 UINTVAL offset, x_funcs, y_funcs;
59 MMD_table * const table = interpreter->binop_mmd_funcs + function;
60 funcptr_t func, def;
62 x_funcs = table->x;
63 y_funcs = table->y;
64 printf(" ");
65 for (x = 0; x < x_funcs; ++x) {
66 if (! (x % 10))
67 printf("%d", (int) x / 10);
68 else
69 printf(" ");
71 printf("\n");
72 for (y = 0; y < y_funcs; ++y) {
73 printf("%3d ", (int)y);
74 for (x = 0; x < x_funcs; ++x) {
75 offset = x_funcs * y + x;
76 func = table->mmd_funcs[offset];
77 printf("%c",
78 func == def ? '.' :
79 (UINTVAL)func & 1 ? 'P' :
80 !func ? '0' : 'F');
82 printf("\n");
84 for (y = 0; y < y_funcs; ++y) {
85 for (x = 0; x < x_funcs; ++x) {
86 offset = x_funcs * y + x;
87 func = table->mmd_funcs[offset];
88 if (func && func != def && !((UINTVAL) func & 1))
89 printf("%3d %3d: %p\n", (int)x, (int)y, (void*) func);
93 #endif
96 funcptr_t
97 get_mmd_dispatch_type(Interp *interpreter, INTVAL func_nr, INTVAL left_type,
98 INTVAL right_type, int *is_pmc)
100 funcptr_t func, func_;
101 UINTVAL offset, x_funcs, y_funcs;
102 INTVAL r;
103 MMD_table *table = interpreter->binop_mmd_funcs + func_nr;
104 x_funcs = table->x;
105 y_funcs = table->y;
107 #if MMD_DEBUG
108 fprintf(stderr, "running function %d with left type=%u, right type=%u\n",
109 (int) func_nr, (unsigned) left_type, (unsigned) right_type);
110 #endif
112 func = NULL;
113 assert(left_type >= 0);
114 assert(right_type >=0 ||
115 (right_type >= enum_type_INTVAL && right_type <= enum_type_PMC));
116 r = right_type;
117 if (right_type < 0)
118 right_type -= enum_type_INTVAL;
119 else
120 right_type += 4;
121 if ((UINTVAL)left_type < x_funcs && (UINTVAL)right_type < y_funcs) {
122 offset = x_funcs * right_type + left_type;
123 func = table->mmd_funcs[offset];
125 if (!func) {
126 const char *meth_c = Parrot_MMD_method_name(interpreter, func_nr);
127 STRING *meth_s = const_string(interpreter, meth_c);
128 PMC *method = Parrot_MMD_search_default_infix(interpreter,
129 meth_s, left_type, r);
130 if (!method)
131 real_exception(interpreter, 0, 1, "MMD function %s not found "
132 "for types (%d, %d)", meth_c, left_type, r);
133 if (method->vtable->base_type == enum_class_NCI) {
134 /* C function is at struct_val */
135 func = D2FPTR(PMC_struct_val(method));
136 *is_pmc = 0;
137 mmd_register(interpreter, func_nr, left_type, r,
138 PMC_struct_val(method));
140 else {
141 *is_pmc = 1;
142 func = D2FPTR(method);
143 mmd_register_sub(interpreter, func_nr, left_type, r, method);
145 return func;
147 *is_pmc = (UINTVAL)func & 3;
148 func_ = (funcptr_t)((UINTVAL)func & ~3);
149 #ifndef PARROT_HAS_ALIGNED_FUNCPTR
150 if (!*is_pmc) {
151 return func;
153 else if (!is_pmc_ptr(interpreter, F2DPTR(func_))) {
154 *is_pmc = 0;
155 return func;
157 #endif
158 return func_;
162 static funcptr_t
163 get_mmd_dispatcher(Interp *interpreter, PMC *left, PMC * right,
164 INTVAL function, int *is_pmc)
166 UINTVAL left_type, right_type;
167 left_type = VTABLE_type(interpreter, left);
168 right_type = VTABLE_type(interpreter, right);
169 return get_mmd_dispatch_type(interpreter, function, left_type, right_type,
170 is_pmc);
175 =item C<static PMC*
176 mmd_deref(Interp *interpreter, INTVAL function, PMC *value)>
178 If C<value> is a reference-like PMC, dereference it so we can make an MMD
179 call on the 'real' value.
181 =cut
185 static PMC *
186 mmd_deref(Interp *interpreter, INTVAL function, PMC *value)
188 if (VTABLE_type(interpreter, value) != value->vtable->base_type)
189 return VTABLE_get_pmc(interpreter, value);
190 else
191 return value;
196 =item C<static void
197 mmd_ensure_writable(Interp *, INTVAL function, PMC *pmc)>
199 Make sure C<pmc> is writable enough for C<function>.
201 =cut
205 static void
206 mmd_ensure_writable(Interp *interpreter, INTVAL function, PMC *pmc) {
207 if (!PMC_IS_NULL(pmc) && (pmc->vtable->flags & VTABLE_IS_READONLY_FLAG))
208 real_exception(interpreter, 0, 1, "%s applied to read-only argument",
209 Parrot_MMD_method_name(interpreter, function));
215 =item C<PMC*
216 mmd_dispatch_p_ppp(Interp *,
217 PMC *left, PMC *right, PMC *dest, INTVAL function)>
219 Dispatch to a multimethod that returns a PMC. C<left>, C<right>, and
220 C<dest> are all PMC pointers, while C<func_num> is the MMD table that
221 should be used to do the dispatching.
222 If the C<dest> pointer is NULL, it dispatches two a two-argument function
223 that returns a new C<dest> always.
225 The MMD system will figure out which function should be called based on
226 the types of C<left> and C<right> and call it, passing in C<left>,
227 C<right>, and possibly C<dest> like any other binary vtable function.
229 =item C<PMC*
230 mmd_dispatch_p_pip(Interp *,
231 PMC *left, INTVAL right, PMC *dest, INTVAL function)>
233 Like above, right argument is a native INTVAL.
235 =item C<PMC*
236 mmd_dispatch_p_pnp(Interp *,
237 PMC *left, FLOATVAL right, PMC *dest, INTVAL function)>
239 Like above, right argument is a native FLOATVAL.
241 =item C<PMC*
242 mmd_dispatch_p_psp(Interp *,
243 PMC *left, STRING *right, PMC *dest, INTVAL function)>
245 Like above, right argument is a native STRING *.
247 =cut
249 =item C<void
250 mmd_dispatch_v_pp(Interp *, PMC *left, PMC *right, INTVAL function)>
252 =item C<void
253 mmd_dispatch_v_pi(Interp *, PMC *left, INTVAL right, INTVAL function)>
255 =item C<void
256 mmd_dispatch_v_pn(Interp *, PMC *left, FLOATVAL right, INTVAL function)>
258 =item C<void
259 mmd_dispatch_v_ps(Interp *, PMC *left, STRING *right, INTVAL function)>
261 Inplace dispatch functions for C<< left <op=> right >>.
265 PMC*
266 mmd_dispatch_p_ppp(Interp *interpreter,
267 PMC *left, PMC *right, PMC *dest, INTVAL func_nr)
269 mmd_f_p_ppp real_function;
270 PMC *sub;
271 int is_pmc;
273 left = mmd_deref(interpreter, func_nr, left);
274 right = mmd_deref(interpreter, func_nr, right);
276 real_function = (mmd_f_p_ppp)get_mmd_dispatcher(interpreter,
277 left, right, func_nr, &is_pmc);
279 if (is_pmc) {
280 sub = (PMC*)real_function;
281 if (dest)
282 return Parrot_runops_fromc_args(interpreter, sub, "PPPP",
283 left, right, dest);
284 else
285 return Parrot_runops_fromc_args(interpreter, sub, "PPP",
286 left, right);
288 else {
289 return (*real_function)(interpreter, left, right, dest);
293 PMC*
294 mmd_dispatch_p_pip(Interp *interpreter,
295 PMC *left, INTVAL right, PMC *dest, INTVAL func_nr)
297 int is_pmc;
299 UINTVAL left_type;
300 mmd_f_p_pip real_function;
302 left = mmd_deref(interpreter, func_nr, left);
304 left_type = left->vtable->base_type;
306 real_function =
307 (mmd_f_p_pip)get_mmd_dispatch_type(interpreter, func_nr,
308 left_type, enum_type_INTVAL,
309 &is_pmc);
311 if (is_pmc) {
312 PMC * const sub = (PMC*)real_function;
313 if (dest)
314 return Parrot_runops_fromc_args(interpreter, sub, "PPIP",
315 left, right, dest);
316 else
317 return Parrot_runops_fromc_args(interpreter, sub, "PPI",
318 left, right);
320 else {
321 return (*real_function)(interpreter, left, right, dest);
325 PMC*
326 mmd_dispatch_p_pnp(Interp *interpreter,
327 PMC *left, FLOATVAL right, PMC *dest, INTVAL func_nr)
329 mmd_f_p_pnp real_function;
330 PMC *sub;
331 int is_pmc;
332 UINTVAL left_type;
334 left = mmd_deref(interpreter, func_nr, left);
336 left_type = left->vtable->base_type;
337 real_function = (mmd_f_p_pnp)get_mmd_dispatch_type(interpreter,
338 func_nr, left_type, enum_type_FLOATVAL, &is_pmc);
339 if (is_pmc) {
340 sub = (PMC*)real_function;
341 if (dest)
342 return Parrot_runops_fromc_args(interpreter, sub, "PPNP",
343 left, right, dest);
344 else
345 return Parrot_runops_fromc_args(interpreter, sub, "PPN",
346 left, right);
348 else {
349 return (*real_function)(interpreter, left, right, dest);
353 PMC*
354 mmd_dispatch_p_psp(Interp *interpreter,
355 PMC *left, STRING *right, PMC *dest, INTVAL func_nr)
357 mmd_f_p_psp real_function;
358 PMC *sub;
359 int is_pmc;
360 UINTVAL left_type;
362 left_type = left->vtable->base_type;
363 real_function = (mmd_f_p_psp)get_mmd_dispatch_type(interpreter,
364 func_nr, left_type, enum_type_STRING, &is_pmc);
365 if (is_pmc) {
366 sub = (PMC*)real_function;
367 if (dest)
368 return Parrot_runops_fromc_args(interpreter, sub, "PPSP",
369 left, right, dest);
370 else
371 return Parrot_runops_fromc_args(interpreter, sub, "PPS",
372 left, right);
374 else {
375 return (*real_function)(interpreter, left, right, dest);
380 * inplace variants
382 void
383 mmd_dispatch_v_pp(Interp *interpreter,
384 PMC *left, PMC *right, INTVAL func_nr)
386 mmd_f_v_pp real_function;
387 PMC *sub;
388 int is_pmc;
391 left = mmd_deref(interpreter, func_nr, left);
392 right = mmd_deref(interpreter, func_nr, right);
394 mmd_ensure_writable(interpreter, func_nr, left);
396 real_function = (mmd_f_v_pp)get_mmd_dispatcher(interpreter,
397 left, right, func_nr, &is_pmc);
399 if (is_pmc) {
400 sub = (PMC*)real_function;
401 Parrot_runops_fromc_args(interpreter, sub, "vPP", left, right);
403 else {
404 (*real_function)(interpreter, left, right);
408 void
409 mmd_dispatch_v_pi(Interp *interpreter,
410 PMC *left, INTVAL right, INTVAL func_nr)
412 mmd_f_v_pi real_function;
413 PMC *sub;
414 int is_pmc;
415 UINTVAL left_type;
417 left = mmd_deref(interpreter, func_nr, left);
418 mmd_ensure_writable(interpreter, func_nr, left);
420 left_type = left->vtable->base_type;
421 real_function = (mmd_f_v_pi)get_mmd_dispatch_type(interpreter,
422 func_nr, left_type, enum_type_INTVAL, &is_pmc);
423 if (is_pmc) {
424 sub = (PMC*)real_function;
425 Parrot_runops_fromc_args(interpreter, sub, "vPI", left, right);
427 else {
428 (*real_function)(interpreter, left, right);
432 void
433 mmd_dispatch_v_pn(Interp *interpreter,
434 PMC *left, FLOATVAL right, INTVAL func_nr)
436 mmd_f_v_pn real_function;
437 PMC *sub;
438 int is_pmc;
439 UINTVAL left_type;
441 left = mmd_deref(interpreter, func_nr, left);
442 mmd_ensure_writable(interpreter, func_nr, left);
444 left_type = left->vtable->base_type;
445 real_function = (mmd_f_v_pn)get_mmd_dispatch_type(interpreter,
446 func_nr, left_type, enum_type_FLOATVAL, &is_pmc);
447 if (is_pmc) {
448 sub = (PMC*)real_function;
449 Parrot_runops_fromc_args(interpreter, sub, "vPN", left, right);
451 else {
452 (*real_function)(interpreter, left, right);
456 void
457 mmd_dispatch_v_ps(Interp *interpreter,
458 PMC *left, STRING *right, INTVAL func_nr)
460 mmd_f_v_ps real_function;
461 PMC *sub;
462 int is_pmc;
463 UINTVAL left_type;
465 left = mmd_deref(interpreter, func_nr, left);
466 mmd_ensure_writable(interpreter, func_nr, left);
468 left_type = VTABLE_type(interpreter, left);
469 real_function = (mmd_f_v_ps)get_mmd_dispatch_type(interpreter,
470 func_nr, left_type, enum_type_STRING, &is_pmc);
471 if (is_pmc) {
472 sub = (PMC*)real_function;
473 Parrot_runops_fromc_args(interpreter, sub, "vPS", left, right);
475 else {
476 (*real_function)(interpreter, left, right);
482 =item C<INTVAL
483 mmd_dispatch_i_pp(Interp *interpreter,
484 PMC *left, PMC *right, INTVAL func_nr)>
486 Like C<mmd_dispatch_p_ppp()>, only it returns an C<INTVAL>. This is used
487 by MMD compare functions.
489 =cut
493 INTVAL
494 mmd_dispatch_i_pp(Interp *interpreter,
495 PMC *left, PMC *right, INTVAL func_nr)
497 mmd_f_i_pp real_function;
498 PMC *sub;
499 int is_pmc;
500 INTVAL ret;
502 left = mmd_deref(interpreter, func_nr, left);
503 right = mmd_deref(interpreter, func_nr, right);
505 real_function = (mmd_f_i_pp)get_mmd_dispatcher(interpreter,
506 left, right, func_nr, &is_pmc);
508 if (is_pmc) {
509 sub = (PMC*)real_function;
510 ret = Parrot_runops_fromc_args_reti(interpreter, sub, "IPP",
511 left, right);
513 else {
514 ret = (*real_function)(interpreter, left, right);
516 return ret;
520 Parrot_run_maybe_mmd_meth(Interp* interpreter, PMC *object,
521 STRING *meth)
524 #if 1
525 return 0; /* TODO */
526 #else
527 INTVAL mmd_func;
528 char *c_meth, *c_sig;
529 int ret = 0, inplace, compare;
533 * check if it's a known MMD function
535 c_meth = string_to_cstring(interpreter, meth);
536 c_sig = string_to_cstring(interpreter, sig);
537 if ( (mmd_func = Parrot_MMD_method_idx(interpreter, c_meth)) >= 0) {
538 /* yep - run it instantly */
539 ret = 1;
540 inplace = c_meth[2] == 'i' && c_meth[3] == '_';
541 compare = mmd_func >= MMD_EQ && mmd_func <= MMD_STRCMP;
542 assert(c_sig[0] == 'O');
543 switch (c_sig[1]) {
544 case 'P':
545 if (inplace)
546 mmd_dispatch_v_pp(interpreter,
547 object, REG_PMC(5), mmd_func);
548 else if (compare)
549 REG_INT(5) = mmd_dispatch_i_pp(interpreter,
550 object, REG_PMC(5), mmd_func);
551 else
552 REG_PMC(5) = mmd_dispatch_p_ppp(interpreter,
553 object, REG_PMC(5), NULL, mmd_func);
554 break;
555 case 'I':
556 if (inplace)
557 mmd_dispatch_v_pi(interpreter,
558 object, REG_INT(5), mmd_func);
559 else
560 REG_PMC(5) = mmd_dispatch_p_pip(interpreter,
561 object, REG_INT(5), NULL, mmd_func);
562 break;
563 case 'N':
564 if (inplace)
565 mmd_dispatch_v_pn(interpreter,
566 object, REG_NUM(5), mmd_func);
567 else
568 REG_PMC(5) = mmd_dispatch_p_pnp(interpreter,
569 object, REG_NUM(5), NULL, mmd_func);
570 break;
571 case 'S':
572 if (inplace)
573 mmd_dispatch_v_ps(interpreter,
574 object, REG_STR(5), mmd_func);
575 else
576 REG_PMC(5) = mmd_dispatch_p_psp(interpreter,
577 object, REG_STR(5), NULL, mmd_func);
578 break;
582 string_cstring_free(c_meth);
583 string_cstring_free(c_sig);
584 return ret;
585 #endif
591 =item C<void
592 mmd_add_function(Interp *interpreter,
593 INTVAL funcnum, funcptr_t function)>
595 Add a new binary MMD function to the list of functions the MMD system knows
596 of. C<func_num> is the number of the new function. C<function> is ignored.
598 TODO change this to a MMD register interface that takes a function *name*.
600 =cut
604 void
605 mmd_add_function(Interp *interpreter,
606 INTVAL func_nr, funcptr_t function)
608 INTVAL i;
609 if (func_nr >= (INTVAL)interpreter->n_binop_mmd_funcs) {
610 if (interpreter->binop_mmd_funcs) {
611 interpreter->binop_mmd_funcs =
612 mem_sys_realloc(interpreter->binop_mmd_funcs,
613 (func_nr + 1) * sizeof(MMD_table));
615 else {
616 interpreter->binop_mmd_funcs =
617 mem_sys_allocate((func_nr + 1) * sizeof(MMD_table));
620 for (i = interpreter->n_binop_mmd_funcs; i <= func_nr; ++i) {
621 MMD_table *table = interpreter->binop_mmd_funcs + i;
622 table->x = table->y = 0;
623 table->mmd_funcs = NULL;
625 interpreter->n_binop_mmd_funcs = func_nr + 1;
632 =item C<static void
633 mmd_expand_x(Interp *interpreter, INTVAL func_nr, INTVAL new_x)>
635 Expands the function table in the X dimension to include C<new_x>.
637 =cut
641 static void
642 mmd_expand_x(Interp *interpreter, INTVAL func_nr, INTVAL new_x)
644 funcptr_t *new_table;
645 UINTVAL x;
646 UINTVAL y;
647 UINTVAL i;
648 MMD_table *table = interpreter->binop_mmd_funcs + func_nr;
649 char *src_ptr, *dest_ptr;
650 size_t old_dp, new_dp;
652 /* Is the Y 0? If so, nothing to expand, so just set the X for
653 later use */
654 if (table->y == 0) {
655 table->x = new_x;
656 return;
659 /* The Y is not zero. Bleah. This means we have to expand the
660 table in an unpleasant way. */
662 x = table->x;
663 y = table->y;
665 /* First, fill in the whole new table with the default function
666 pointer. We only really need to do the new part, but... */
667 new_table = mem_sys_allocate(sizeof(funcptr_t) * y * new_x);
668 for (i = 0; i < y * new_x; i++) {
669 new_table[i] = NULL;
672 /* Then copy the old table over. We have to do this row by row,
673 because the rows in the old and new tables are different
674 lengths */
675 src_ptr = (char*) table->mmd_funcs;
676 dest_ptr = (char*) new_table;
677 old_dp = sizeof(funcptr_t) * x;
678 new_dp = sizeof(funcptr_t) * new_x;
679 for (i = 0; i < y; i++) {
680 memcpy(dest_ptr, src_ptr, sizeof(funcptr_t) * x);
681 src_ptr += old_dp;
682 dest_ptr += new_dp;
684 if (table->mmd_funcs)
685 mem_sys_free(table->mmd_funcs);
686 table->x = new_x;
687 /* Set the old table to point to the new table */
688 table->mmd_funcs = new_table;
693 =item C<static void
694 mmd_expand_y(Interp *interpreter, INTVAL func_nr, INTVAL new_y)>
696 Expands the function table in the Y direction.
698 =cut
702 static void
703 mmd_expand_y(Interp *interpreter, INTVAL func_nr, INTVAL new_y)
705 funcptr_t *new_table;
706 UINTVAL x;
707 UINTVAL y;
708 UINTVAL i;
709 MMD_table *table = interpreter->binop_mmd_funcs + func_nr;
711 x = table->x;
712 assert(x);
713 y = table->y;
715 /* First, fill in the whole new table with the default function
716 pointer. We only really need to do the new part, but... */
717 new_table = mem_sys_allocate(sizeof(funcptr_t) * x * new_y);
718 for (i = 0; i < x * new_y; i++) {
719 new_table[i] = NULL;
722 /* Then copy the old table over, if it existed in the first place. */
723 if (table->mmd_funcs) {
724 memcpy(new_table, table->mmd_funcs,
725 sizeof(funcptr_t) * x * y);
726 mem_sys_free(table->mmd_funcs);
728 table->y = new_y;
729 table->mmd_funcs = new_table;
735 =item C<void
736 mmd_add_by_class(Interp *interpreter,
737 INTVAL functype,
738 STRING *left_class, STRING *right_class,
739 funcptr_t funcptr)>
741 Add a function to the MMD table by class name, rather than class number.
742 Handles the case where the named class isn't loaded yet.
744 Adds a new MMD function C<funcptr> to the C<func_num> function table
745 that will be invoked when the left parameter is of class C<left_class>
746 and the right parameter is of class C<right_class>. Both classes are
747 C<STRING *>s that hold the PMC class names for the left and right sides.
748 If either class isn't yet loaded, Parrot will cache the information such
749 that the function will be installed if at some point in the future both
750 classes are available.
752 Currently this is done by just assigning class numbers to the classes,
753 which the classes will pick up and use if they're later loaded, but we
754 may later put the functions into a deferred table that we scan when PMC
755 classes are loaded. Either way, the function will be guaranteed to be
756 installed when it's needed.
758 The function table must exist, but if it is too small, it will
759 automatically be expanded.
761 =cut
765 void
766 mmd_add_by_class(Interp *interpreter,
767 INTVAL functype,
768 STRING *left_class, STRING *right_class,
769 funcptr_t funcptr)
771 INTVAL left_type = pmc_type(interpreter, left_class);
772 INTVAL right_type = pmc_type(interpreter, right_class);
774 if (left_type == enum_type_undef) {
775 left_type = pmc_register(interpreter, left_class);
777 if (right_type == enum_type_undef) {
778 right_type = pmc_register(interpreter, right_class);
781 mmd_register(interpreter, functype, left_type, right_type, funcptr);
787 =item C<void
788 mmd_register(Interp *interpreter,
789 INTVAL func_num,
790 INTVAL left_type, INTVAL right_type,
791 funcptr_t funcptr)>
793 Register a function C<funcptr> for MMD function table C<func_num> for classes
794 C<left_type> and C<right_type>. The left and right types are C<INTVAL>s that
795 represent the class ID numbers.
797 The function table must exist, but if it is too small, it will
798 automatically be expanded.
800 Adding a new function to the table can be interestingly non-trivial, so
801 we get to be tricky.
803 If the left or right types are larger than anything we've seen so far,
804 it means that we have to expand the table. Making Y larger is simple --
805 just realloc with some more rows. Making X larger is less simple. In
806 either case, we punt to other functions.
808 TODO - Currently the MMD system doesn't handle inheritance and best match
809 searching, as it assumes that all PMC types have no parent type. This
810 can be considered a bug, and will be resolved at some point in the
811 future.
813 =cut
817 void
818 mmd_register(Interp *interpreter,
819 INTVAL func_nr,
820 INTVAL left_type, INTVAL right_type,
821 funcptr_t funcptr)
824 INTVAL offset;
825 MMD_table *table;
827 assert(func_nr < (INTVAL)interpreter->n_binop_mmd_funcs);
828 assert(left_type >= 0);
829 assert(right_type >=0 ||
830 (right_type >= enum_type_INTVAL && right_type <= enum_type_PMC));
831 if (right_type < 0)
832 right_type -= enum_type_INTVAL;
833 else
834 right_type += 4;
835 table = interpreter->binop_mmd_funcs + func_nr;
836 if ((INTVAL)table->x <= left_type) {
837 mmd_expand_x(interpreter, func_nr, left_type + 1);
840 if ((INTVAL)table->y <= right_type) {
841 mmd_expand_y(interpreter, func_nr, right_type + 1);
844 offset = table->x * right_type + left_type;
845 table->mmd_funcs[offset] = funcptr;
848 void
849 mmd_register_sub(Interp *interpreter,
850 INTVAL func_nr,
851 INTVAL left_type, INTVAL right_type,
852 PMC *sub)
854 PMC *fake;
855 if (sub->vtable->base_type == enum_class_CSub) {
856 /* returned from mmdvt_find */
857 mmd_register(interpreter, func_nr, left_type, right_type,
858 D2FPTR(PMC_struct_val(sub)));
860 else {
861 fake = (PMC*)((UINTVAL) sub | 1);
862 mmd_register(interpreter, func_nr, left_type, right_type, D2FPTR(fake));
868 =item C<void
869 mmd_destroy(Parrot_Interp interpreter)>
871 Frees all the memory allocated used the MMD subsystem.
873 =cut
877 void
878 mmd_destroy(Parrot_Interp interpreter)
880 if (interpreter->n_binop_mmd_funcs) {
881 UINTVAL i;
882 for (i = 0; i <interpreter->n_binop_mmd_funcs; ++i) {
883 if (interpreter->binop_mmd_funcs[i].mmd_funcs) {
884 mem_sys_free(interpreter->binop_mmd_funcs[i].mmd_funcs);
885 interpreter->binop_mmd_funcs[i].mmd_funcs = NULL;
889 mem_sys_free(interpreter->binop_mmd_funcs);
890 interpreter->binop_mmd_funcs = NULL;
895 =item C<PMC *
896 mmd_vtfind(Parrot_Interp interpreter, INTVAL type, INTVAL left, INTVAL right)>
898 Return an MMD PMC function for the given data types. The return result is
899 either a Sub PMC (for PASM MMD functions) or a CSub PMC holding the
900 C function pointer in PMC_struct_val. This CSub is not invocable, you have to
901 wrap it into an NCI function to get the required function arguments passed.
903 =cut
907 PMC *
908 mmd_vtfind(Parrot_Interp interpreter, INTVAL func_nr,
909 INTVAL left, INTVAL right) {
910 int is_pmc;
911 PMC *f;
912 funcptr_t func = get_mmd_dispatch_type(interpreter,
913 func_nr, left, right, &is_pmc);
914 if (func && is_pmc) {
915 /* TODO if is_pmc == 2 a Bound_NCI is returned, which actually
916 * should be filled with one of the wrapper functions
918 return (PMC*)F2DPTR(func);
920 f = pmc_new(interpreter, enum_class_CSub);
921 PMC_struct_val(f) = F2DPTR(func);
922 return f;
926 static PMC* mmd_arg_tuple_inline(Interp *, STRING *signature, va_list args);
927 static PMC* mmd_arg_tuple_func(Interp *);
928 static PMC* mmd_search_default(Interp *, STRING *meth, PMC *arg_tuple);
929 static PMC* mmd_search_scopes(Interp *, STRING *meth, PMC *arg_tuple);
930 static void mmd_search_classes(Interp *, STRING *meth, PMC *arg_tuple, PMC *,
931 INTVAL start);
932 static int mmd_search_lexical(Interp *, STRING *meth, PMC *arg_tuple, PMC *);
933 static int mmd_search_package(Interp *, STRING *meth, PMC *arg_tuple, PMC *);
934 static int mmd_search_global(Interp *, STRING *meth, PMC *arg_tuple, PMC *);
935 static void mmd_search_builtin(Interp *, STRING *meth, PMC *arg_tuple, PMC *);
936 static int mmd_maybe_candidate(Interp *, PMC *pmc, PMC *arg_tuple, PMC *cl);
937 static void mmd_sort_candidates(Interp *, PMC *arg_tuple, PMC *cl);
941 =item C<PMC *Parrot_MMD_search_default_inline(Interp *,
942 STRING *meth,
943 STRING *signature, ...)>
945 Default implementation of MMD lookup. The signature contains the letters
946 "INSP" for the argument types. B<PMC> arguments are given in the function call.
948 =item C<PMC *Parrot_MMD_search_default_func(Interp *, STRING *meth)>
950 Default implementation of MMD lookup. The signature contains the letters
951 "INSP" for the argument types. B<PMC> arguments are taken from
952 the argument tuple.
954 =cut
959 * TODO move to header, when API is sane
962 PMC *
963 Parrot_MMD_search_default_inline(Interp *interpreter, STRING *meth,
964 STRING *signature, ...)
966 va_list args;
967 PMC* arg_tuple;
969 * 1) create argument tuple
971 va_start(args, signature);
972 arg_tuple = mmd_arg_tuple_inline(interpreter, signature, args);
973 va_end(args);
975 * default search policy
977 return mmd_search_default(interpreter, meth, arg_tuple);
980 PMC *
981 Parrot_MMD_search_default_func(Interp *interpreter, STRING *meth)
983 PMC* arg_tuple;
985 * 1) create argument tuple
987 arg_tuple = mmd_arg_tuple_func(interpreter);
989 * default search policy
991 return mmd_search_default(interpreter, meth, arg_tuple);
994 PMC *
995 Parrot_MMD_search_default_infix(Interp *interpreter, STRING *meth,
996 INTVAL left_type, INTVAL right_type)
998 PMC* arg_tuple;
1000 arg_tuple = pmc_new(interpreter, enum_class_FixedIntegerArray);
1001 VTABLE_set_integer_native(interpreter, arg_tuple, 2);
1002 VTABLE_set_integer_keyed_int(interpreter, arg_tuple, 0, left_type);
1003 VTABLE_set_integer_keyed_int(interpreter, arg_tuple, 1, right_type);
1004 return mmd_search_default(interpreter, meth, arg_tuple);
1009 =item C<PMC* Parrot_MMD_dispatch_func(Interp *, PMC *multi)>
1011 Given a multi sub PMC (usually the multi method of one class) return the
1012 best matching function for the call signature and call arguments according
1013 to pdd03.
1015 =cut
1019 PMC *
1020 Parrot_MMD_dispatch_func(Interp *interpreter, PMC *multi, STRING *meth)
1022 PMC* arg_tuple, *pmc;
1023 PMC *candidate_list;
1024 INTVAL n;
1026 * 1) create argument tuple
1028 arg_tuple = mmd_arg_tuple_func(interpreter);
1030 n = VTABLE_elements(interpreter, multi);
1031 if (!n)
1032 return NULL;
1034 candidate_list = VTABLE_clone(interpreter, multi);
1036 * 4) go through all parents of MRO and check for methods
1037 * where the first argument matches
1039 * XXX do we need this?
1041 if (meth)
1042 mmd_search_classes(interpreter, meth, arg_tuple, candidate_list, 1);
1044 * 5) sort the list
1046 if (n > 1)
1047 mmd_sort_candidates(interpreter, arg_tuple, candidate_list);
1048 n = VTABLE_elements(interpreter, candidate_list);
1049 if (!n)
1050 return NULL;
1052 * 6) Uff, return first one
1054 pmc = VTABLE_get_pmc_keyed_int(interpreter, candidate_list, 0);
1055 return pmc;
1060 =item C<
1061 static PMC* mmd_arg_tuple_inline(Interp *, STRING *signature, va_list args)>
1063 Return a list of argument types. PMC arguments are specified as function
1064 arguments.
1066 =item C<
1067 static PMC* mmd_arg_tuple_func(Interp *)>
1069 Return a list of argument types. PMC arguments are take from registers
1070 according to calling conventions.
1072 =cut
1076 static PMC*
1077 mmd_arg_tuple_inline(Interp *interpreter, STRING *signature, va_list args)
1079 INTVAL sig_len, i, type;
1080 PMC* arg_tuple, *arg;
1082 arg_tuple = pmc_new(interpreter, enum_class_FixedIntegerArray);
1083 sig_len = string_length(interpreter, signature);
1084 if (!sig_len)
1085 return arg_tuple;
1086 VTABLE_set_integer_native(interpreter, arg_tuple, sig_len);
1087 for (i = 0; i < sig_len; ++i) {
1088 type = string_index(interpreter, signature, i);
1089 switch (type) {
1090 case 'I':
1091 VTABLE_set_integer_keyed_int(interpreter, arg_tuple,
1092 i, enum_type_INTVAL);
1093 break;
1094 case 'N':
1095 VTABLE_set_integer_keyed_int(interpreter, arg_tuple,
1096 i, enum_type_FLOATVAL);
1097 break;
1098 case 'S':
1099 VTABLE_set_integer_keyed_int(interpreter, arg_tuple,
1100 i, enum_type_STRING);
1101 break;
1102 case 'O':
1103 case 'P':
1104 arg = va_arg(args, PMC *);
1105 type = VTABLE_type(interpreter, arg);
1106 VTABLE_set_integer_keyed_int(interpreter, arg_tuple,
1107 i, type);
1108 break;
1109 default:
1110 internal_exception(1,
1111 "Unknown signature type %d in mmd_arg_tuple", type);
1112 break;
1116 return arg_tuple;
1119 static PMC*
1120 mmd_arg_tuple_func(Interp *interpreter)
1122 INTVAL sig_len, i, type, idx;
1123 PMC* arg_tuple, *arg;
1124 PMC* args_array; /* from recent set_args opcode */
1125 opcode_t *args_op;
1126 struct PackFile_Constant **constants;
1129 * if there is no signature e.g. because of
1130 * m = getattribute l, "__add"
1131 * - we have to return the MultiSub
1132 * - create a BoundMulit
1133 * - dispatch in invoke - yeah ugly
1136 arg_tuple = pmc_new(interpreter, enum_class_ResizableIntegerArray);
1137 args_op = interpreter->current_args;
1138 if (!args_op)
1139 return arg_tuple;
1140 assert(*args_op == PARROT_OP_set_args_pc);
1141 constants = interpreter->code->const_table->constants;
1142 ++args_op;
1143 args_array = constants[*args_op]->u.key;
1144 ASSERT_SIG_PMC(args_array);
1145 sig_len = SIG_ELEMS(args_array);
1146 if (!sig_len)
1147 return arg_tuple;
1148 ++args_op;
1150 for (i = 0; i < sig_len; ++i, ++args_op) {
1151 type = SIG_ITEM(args_array, i);
1152 /* named don't MMD */
1153 if (type & PARROT_ARG_NAME)
1154 break;
1155 /* expand flattening args */
1156 if (type & PARROT_ARG_FLATTEN) {
1157 int j, n;
1159 idx = *args_op;
1160 arg = REG_PMC(idx);
1161 n = VTABLE_elements(interpreter, arg);
1162 for (j = 0; j < n; ++j) {
1163 PMC *elem = VTABLE_get_pmc_keyed_int(interpreter, arg, j);
1164 type = VTABLE_type(interpreter, elem);
1165 VTABLE_push_integer(interpreter, arg_tuple, type);
1167 return arg_tuple;
1169 switch (type & PARROT_ARG_TYPE_MASK) {
1170 case PARROT_ARG_INTVAL:
1171 VTABLE_push_integer(interpreter, arg_tuple, enum_type_INTVAL);
1172 break;
1173 case PARROT_ARG_FLOATVAL:
1174 VTABLE_push_integer(interpreter, arg_tuple, enum_type_FLOATVAL);
1175 break;
1176 case PARROT_ARG_STRING:
1177 VTABLE_push_integer(interpreter, arg_tuple, enum_type_STRING);
1178 break;
1179 case PARROT_ARG_PMC:
1180 idx = *args_op;
1181 if ((type & PARROT_ARG_CONSTANT))
1182 arg = constants[idx]->u.key;
1183 else
1184 arg = REG_PMC(idx);
1185 type = VTABLE_type(interpreter, arg);
1186 VTABLE_push_integer(interpreter, arg_tuple, type);
1187 break;
1188 default:
1189 internal_exception(1,
1190 "Unknown signature type %d in mmd_arg_tuple", type);
1191 break;
1195 return arg_tuple;
1200 =item C<static PMC* mmd_search_default(Interp *, STRING *meth, PMC *arg_tuple)>
1202 Default implementation of MMD search. Search scopes for candidates, walk the
1203 class hierarchy, sort all candidates by their Manhattan distance, and return
1204 result
1206 =cut
1210 static PMC*
1211 mmd_search_default(Interp *interpreter, STRING *meth, PMC *arg_tuple)
1213 PMC *candidate_list, *pmc;
1214 INTVAL n;
1215 STRING *_sub;
1218 * 2) create a list of matching functions
1220 candidate_list = mmd_search_scopes(interpreter, meth, arg_tuple);
1222 * 3) if list is empty fail
1223 * if the first found function is a plain Sub: finito
1225 n = VTABLE_elements(interpreter, candidate_list);
1226 if (n == 1) {
1227 pmc = VTABLE_get_pmc_keyed_int(interpreter, candidate_list, 0);
1228 _sub = CONST_STRING(interpreter, "Sub");
1230 if (VTABLE_isa(interpreter, pmc, _sub)) {
1231 return pmc;
1235 * 4) first was a MultiSub - go through all found MultiSubs and check
1236 * the first arguments MRO, add all MultiSubs and plain methods,
1237 * where the first argument matches
1239 mmd_search_classes(interpreter, meth, arg_tuple, candidate_list, 0);
1240 n = VTABLE_elements(interpreter, candidate_list);
1241 if (!n)
1242 return NULL;
1244 * 5) sort the list
1246 if (n > 1)
1247 mmd_sort_candidates(interpreter, arg_tuple, candidate_list);
1248 n = VTABLE_elements(interpreter, candidate_list);
1249 if (!n)
1250 return NULL;
1252 * 6) Uff, return first one
1254 pmc = VTABLE_get_pmc_keyed_int(interpreter, candidate_list, 0);
1255 return pmc;
1260 =item C<static void mmd_search_classes(Interp *, STRING *meth,
1261 PMC *arg_tuple, PMC *cl,
1262 INTVAL start_at_parent)>
1264 Search all the classes in all MultiSubs of the candidates C<cl> and return
1265 a list of all candidates. C<start_at_parent> is 0 to start at the class itself
1266 or 1 to search from the first parent class.
1268 =cut
1272 static void
1273 mmd_search_classes(Interp *interpreter, STRING *meth, PMC *arg_tuple,
1274 PMC *cl, INTVAL start_at_parent)
1276 PMC *pmc, *mro, *class;
1277 INTVAL i, n, type1;
1280 * get the class of the first argument
1282 if (!VTABLE_elements(interpreter, arg_tuple))
1283 return;
1284 type1 = VTABLE_get_integer_keyed_int(interpreter, arg_tuple, 0);
1285 if (type1 < 0) {
1286 return;
1287 internal_exception(1, "unimplemented native MMD type");
1288 /* TODO create some class namespace */
1290 else {
1291 mro = interpreter->vtables[type1]->mro;
1292 n = VTABLE_elements(interpreter, mro);
1293 for (i = start_at_parent; i < n; ++i) {
1294 class = VTABLE_get_pmc_keyed_int(interpreter, mro, i);
1295 pmc = Parrot_find_method_with_cache(interpreter, class, meth);
1296 if (pmc) {
1298 * mmd_is_hidden would consider all previous candidates
1299 * XXX pass current n so that only candidates from this
1300 * mro are used?
1302 if (mmd_maybe_candidate(interpreter, pmc, arg_tuple, cl))
1303 break;
1309 static INTVAL
1310 distance_cmp(Interp *interpreter, INTVAL a, INTVAL b)
1312 short da = (short)a & 0xffff;
1313 short db = (short)b & 0xffff;
1314 /* sort first by distance */
1315 if (da > db)
1316 return 1;
1317 if (da < db)
1318 return -1;
1319 /* end then by index in candidate list */
1320 da = (short)(a >> 16);
1321 db = (short)(b >> 16);
1322 return da > db ? 1 : da < db ? -1 : 0;
1325 extern void Parrot_FixedPMCArray_sort(Interp* , PMC* pmc, PMC *cmp_func);
1329 =item C<static UINTVAL mmd_distance(Interp *, PMC *pmc, PMC *arg_tuple)>
1331 Create Manhattan Distance of sub C<pmc> against given argument types.
1332 0xffff is the maximum distance
1334 =cut
1338 static PMC*
1339 mmd_cvt_to_types(Interp* interpreter, PMC *multi_sig)
1341 INTVAL i, n, type;
1342 PMC *ar, *sig_elem;
1343 STRING *sig;
1345 n = VTABLE_elements(interpreter, multi_sig);
1346 ar = pmc_new(interpreter, enum_class_FixedIntegerArray);
1347 VTABLE_set_integer_native(interpreter, ar, n);
1348 for (i = 0; i < n; ++i) {
1349 sig_elem = VTABLE_get_pmc_keyed_int(interpreter, multi_sig, i);
1350 if (sig_elem->vtable->base_type == enum_class_String) {
1351 sig = VTABLE_get_string(interpreter, sig_elem);
1352 if (memcmp(sig->strstart, "__VOID", 6) == 0) {
1353 PMC_int_val(ar)--; /* XXX */
1354 break;
1356 type = pmc_type(interpreter, sig);
1358 else {
1359 type = pmc_type_p(interpreter, sig_elem);
1361 VTABLE_set_integer_keyed_int(interpreter, ar, i, type);
1363 return ar;
1366 #define MMD_BIG_DISTANCE 0x7fff
1368 static UINTVAL
1369 mmd_distance(Interp *interpreter, PMC *pmc, PMC *arg_tuple)
1371 PMC *multi_sig, *mro;
1372 INTVAL i, n, args, dist, j, m;
1373 INTVAL type_sig, type_call;
1375 if (pmc->vtable->base_type == enum_class_NCI) {
1376 /* has to be a builtin multi method */
1377 multi_sig = PMC_pmc_val(pmc);
1379 else if (pmc->vtable->base_type == enum_class_Sub) {
1380 multi_sig = PMC_sub(pmc)->multi_signature;
1381 if (!multi_sig) {
1382 /* some method */
1383 return 0;
1385 if (multi_sig->vtable->base_type == enum_class_FixedPMCArray) {
1386 multi_sig = PMC_sub(pmc)->multi_signature =
1387 mmd_cvt_to_types(interpreter, multi_sig);
1390 else
1391 return MMD_BIG_DISTANCE;
1392 n = VTABLE_elements(interpreter, multi_sig);
1393 args = VTABLE_elements(interpreter, arg_tuple);
1395 * arg_tuple may have more arguments - only the
1396 * n multi_sig invocants are counted
1398 if (args < n)
1399 return MMD_BIG_DISTANCE;
1400 dist = 0;
1401 if (args > n)
1402 dist = 1000; /* XXX arbitrary > max_class_depth * n */
1404 * now go through args
1406 for (i = 0; i < n; ++i) {
1407 type_sig = VTABLE_get_integer_keyed_int(interpreter, multi_sig, i);
1408 type_call = VTABLE_get_integer_keyed_int(interpreter, arg_tuple, i);
1409 if (type_sig == type_call)
1410 continue;
1412 * different native types are very different, except a PMC
1413 * which matches any PMC
1415 if ((type_sig <= 0 && type_sig != enum_type_PMC) || type_call <= 0) {
1416 dist = MMD_BIG_DISTANCE;
1417 break;
1420 * now consider MRO of types the signature type has to be somewhere
1421 * in the MRO of the type_call
1423 mro = interpreter->vtables[type_call]->mro;
1424 m = VTABLE_elements(interpreter, mro);
1425 for (j = 0; j < m; ++j) {
1426 PMC *cl = VTABLE_get_pmc_keyed_int(interpreter, mro, j);
1427 if (cl->vtable->base_type == type_sig)
1428 break;
1429 ++dist;
1432 * if the type wasn't in MRO check, if any PMC matches
1433 * in that case use the distance + 1 (of an any PMC parent)
1435 if (j == m && type_sig != enum_type_PMC) {
1436 dist = MMD_BIG_DISTANCE;
1437 break;
1439 ++dist;
1440 #if MMD_DEBUG
1442 STRING *s1, *s2;
1443 if (type_sig < 0)
1444 s1 = Parrot_get_datatype_name(interpreter, type_sig);
1445 else {
1446 s1 = interpreter->vtables[type_sig]->whoami;
1448 if (type_call < 0)
1449 s2 = Parrot_get_datatype_name(interpreter, type_call);
1450 else {
1451 s2 = interpreter->vtables[type_call]->whoami;
1453 PIO_eprintf(interpreter, "arg %d: dist %d sig %Ss arg %Ss\n",
1454 i, dist, s1, s2);
1456 #endif
1458 return dist;
1463 =item C<static void mmd_sort_candidates(Interp *, PMC *arg_tuple, PMC *cl)>
1465 Sort the candidate list C<cl> by Manhattan Distance
1467 =cut
1471 static void
1472 mmd_sort_candidates(Interp *interpreter, PMC *arg_tuple, PMC *cl)
1474 INTVAL i, n, d;
1475 PMC *nci, *pmc, *sort;
1476 INTVAL *helper;
1477 PMC **data;
1479 n = VTABLE_elements(interpreter, cl);
1481 * create a helper structure:
1482 * bits 0..15 = distance
1483 * bits 16..31 = idx in candidate list
1485 * TODO use half of available INTVAL bits
1487 sort = pmc_new(interpreter, enum_class_FixedIntegerArray);
1488 VTABLE_set_integer_native(interpreter, sort, n);
1489 helper = PMC_data(sort);
1490 for (i = 0; i < n; ++i) {
1491 pmc = VTABLE_get_pmc_keyed_int(interpreter, cl, i);
1492 d = mmd_distance(interpreter, pmc, arg_tuple);
1493 helper[i] = i << 16 | (d & 0xffff);
1496 * need an NCI function pointer
1498 nci = pmc_new(interpreter, enum_class_NCI);
1499 PMC_struct_val(nci) = F2DPTR(distance_cmp);
1501 * sort it
1503 Parrot_FixedPMCArray_sort(interpreter, sort, nci);
1505 * now helper has a sorted list of indices in the upper 16 bits
1506 * fill helper with sorted candidates
1508 data = PMC_data(cl);
1509 for (i = 0; i < n; ++i) {
1510 INTVAL idx = helper[i] >> 16;
1512 * if the distance is big stop
1514 if ((helper[i] & 0xffff) == MMD_BIG_DISTANCE) {
1515 PMC_int_val(cl) = i;
1516 break;
1518 helper[i] = (INTVAL)data[idx];
1521 * use helper structure
1523 PMC_data(cl) = helper;
1524 PMC_data(sort) = data;
1529 =item C<static PMC* mmd_search_scopes(Interp *, STRING *meth, PMC *arg_tuple)>
1531 Search all scopes for MMD candidates matching the arguments given in
1532 C<arg_tuple>.
1534 =cut
1538 static PMC*
1539 mmd_search_scopes(Interp *interpreter, STRING *meth, PMC *arg_tuple)
1541 PMC *candidate_list;
1542 int stop;
1544 candidate_list = pmc_new(interpreter, enum_class_ResizablePMCArray);
1546 * XXX disabled during LexPad / ScratchPad transisition
1547 stop = mmd_search_lexical(interpreter, meth, arg_tuple, candidate_list);
1548 if (stop)
1549 return candidate_list;
1551 stop = mmd_search_package(interpreter, meth, arg_tuple, candidate_list);
1552 if (stop)
1553 return candidate_list;
1554 stop = mmd_search_global(interpreter, meth, arg_tuple, candidate_list);
1555 if (stop)
1556 return candidate_list;
1557 mmd_search_builtin(interpreter, meth, arg_tuple, candidate_list);
1558 return candidate_list;
1563 =item C<static int mmd_is_hidden(Interp *, PMC *multi, PMC *cl)>
1565 Check if the given multi sub is hidden by any inner multi sub (already in
1566 the candidate list C<cl>.
1568 =cut
1572 static int
1573 mmd_is_hidden(Interp *interpreter, PMC *multi, PMC *cl)
1576 * if the candidate list already has the a sub with the same
1577 * signature (long name), the outer multi is hidden
1579 * TODO
1581 return 0;
1586 =item C<static int mmd_maybe_candidate(Interp *, PMC *pmc,
1587 PMC *arg_tuple, PMC *cl)>
1589 If the candidate C<pmc> is a Sub PMC, push it on the candidate list and
1590 return TRUE to stop further search.
1592 If the candidate is a MultiSub remember all matching Subs and return FALSE
1593 to continue searching outer scopes.
1595 =cut
1599 static int
1600 mmd_maybe_candidate(Interp *interpreter, PMC *pmc, PMC *arg_tuple, PMC *cl)
1602 STRING *_sub, *_multi_sub;
1603 INTVAL i, n;
1605 _sub = CONST_STRING(interpreter, "Sub");
1606 _multi_sub = CONST_STRING(interpreter, "MultiSub");
1608 if (VTABLE_isa(interpreter, pmc, _sub)) {
1609 /* a plain sub stops outer searches */
1610 /* TODO check arity of sub */
1612 VTABLE_push_pmc(interpreter, cl, pmc);
1613 return 1;
1615 if (!VTABLE_isa(interpreter, pmc, _multi_sub)) {
1616 /* not a Sub or MultiSub - ignore */
1617 return 0;
1620 * ok we have a multi sub pmc, which is an array of candidates
1622 n = VTABLE_elements(interpreter, pmc);
1623 for (i = 0; i < n; ++i) {
1624 PMC *multi_sub;
1626 multi_sub = VTABLE_get_pmc_keyed_int(interpreter, pmc, i);
1627 if (!mmd_is_hidden(interpreter, multi_sub, cl))
1628 VTABLE_push_pmc(interpreter, cl, multi_sub);
1630 return 0;
1635 =item C<static int mmd_search_lexical(Interp *, STRING *meth,
1636 PMC *arg_tuple, PMC *cl)>
1638 Search the current lexical pad for matching candidates. Return TRUE if the
1639 MMD search should stop.
1641 =cut
1645 static int
1646 mmd_search_lexical(Interp *interpreter, STRING *meth, PMC *arg_tuple, PMC *cl)
1648 /* TODO */
1649 return 0;
1654 =item C<static int mmd_search_package(Interp *, STRING *meth,
1655 PMC *arg_tuple, PMC *cl)>
1657 Search the current package namespace for matching candidates. Return
1658 TRUE if the MMD search should stop.
1660 =cut
1664 static int
1665 mmd_search_package(Interp *interpreter, STRING *meth, PMC *arg_tuple, PMC *cl)
1667 PMC *pmc;
1669 pmc = Parrot_find_global_cur(interpreter, meth);
1670 if (pmc) {
1671 if (mmd_maybe_candidate(interpreter, pmc, arg_tuple, cl))
1672 return 1;
1674 return 0;
1679 =item C<static int mmd_search_global(Interp *, STRING *meth,
1680 PMC *arg_tuple, PMC *cl)>
1682 Search the global namespace for matching candidates. Return TRUE if
1683 the MMD search should stop.
1685 =cut
1689 static int
1690 mmd_search_global(Interp *interpreter, STRING *meth, PMC *arg_tuple, PMC *cl)
1692 PMC *pmc;
1694 pmc = Parrot_find_global_n(interpreter, interpreter->root_namespace, meth);
1695 if (pmc) {
1696 if (mmd_maybe_candidate(interpreter, pmc, arg_tuple, cl))
1697 return 1;
1699 return 0;
1704 =item C<static void mmd_search_builtin(Interp *, STRING *meth,
1705 PMC *arg_tuple, PMC *cl)>
1707 Search the builtin namespace for matching candidates. This is the last
1708 search in all the namespaces.
1710 =cut
1714 static PMC*
1715 mmd_get_ns(Interp *interpreter)
1717 STRING *ns_name;
1718 PMC *ns;
1720 ns_name = CONST_STRING(interpreter, "__parrot_core");
1721 ns = Parrot_get_namespace_keyed_str(interpreter,
1722 interpreter->root_namespace, ns_name);
1723 return ns;
1726 static PMC*
1727 mmd_make_ns(Interp *interpreter)
1729 STRING *ns_name;
1730 PMC *ns;
1732 ns_name = CONST_STRING(interpreter, "__parrot_core");
1733 ns = Parrot_make_namespace_keyed_str(interpreter,
1734 interpreter->root_namespace, ns_name);
1735 return ns;
1738 static void
1739 mmd_search_builtin(Interp *interpreter, STRING *meth, PMC *arg_tuple, PMC *cl)
1741 PMC *pmc, *ns;
1742 ns = mmd_get_ns(interpreter);
1743 pmc = Parrot_find_global_n(interpreter, ns, meth);
1744 if (pmc)
1745 mmd_maybe_candidate(interpreter, pmc, arg_tuple, cl);
1749 static PMC *
1750 mmd_create_builtin_multi_stub(Interp *interpreter, PMC* ns, INTVAL func_nr)
1752 const char *name;
1753 STRING *s;
1754 PMC *multi;
1756 name = Parrot_MMD_method_name(interpreter, func_nr);
1757 /* create in constant pool */
1758 s = const_string(interpreter, name);
1759 multi = constant_pmc_new(interpreter, enum_class_MultiSub);
1760 VTABLE_set_pmc_keyed_str(interpreter, ns, s, multi);
1761 return ns;
1764 static void
1765 mmd_create_builtin_multi_meth_2(Interp *interpreter, PMC *ns,
1766 INTVAL func_nr, INTVAL type, INTVAL right, funcptr_t func_ptr)
1768 const char *short_name;
1769 char signature[6], val_sig;
1770 STRING *meth_name, *_sub;
1771 PMC *method, *multi, *class, *multi_sig;
1773 assert (type != enum_class_Null && type != enum_class_delegate &&
1774 type != enum_class_Ref && type != enum_class_SharedRef &&
1775 type != enum_class_deleg_pmc && type != enum_class_ParrotClass &&
1776 type != enum_class_ParrotObject);
1777 short_name = Parrot_MMD_method_name(interpreter, func_nr);
1779 * _int, _float, _str are just native variants of the base
1780 * multi
1782 val_sig = 'P';
1783 if (right == enum_type_INTVAL)
1784 val_sig = 'I';
1785 else if (right == enum_type_STRING)
1786 val_sig = 'S';
1787 else if (right == enum_type_FLOATVAL)
1788 val_sig = 'N';
1791 * create NCI method in left class
1793 strcpy(signature, "PJP.P");
1794 signature[3] = val_sig;
1795 if (func_nr >= MMD_EQ && func_nr <= MMD_STRCMP) {
1796 signature[0] = 'I';
1797 signature[4] = '\0';
1799 /* implace infix like __i_add don't return a result */
1800 if (memcmp(short_name, "__i_", 4) == 0)
1801 signature[0] = 'v';
1802 meth_name = const_string(interpreter, short_name);
1803 class = interpreter->vtables[type]->class;
1804 method = Parrot_find_method_direct(interpreter, class, meth_name);
1805 if (!method) {
1806 /* first method */
1807 method = constant_pmc_new(interpreter, enum_class_NCI);
1808 VTABLE_set_pointer_keyed_str(interpreter, method,
1809 const_string(interpreter, signature),
1810 F2DPTR(func_ptr));
1811 VTABLE_add_method(interpreter, class, meth_name, method);
1813 else {
1814 _sub = CONST_STRING(interpreter, "Sub");
1815 /* multiple methods with that same name */
1816 if (method->vtable->base_type == enum_class_NCI) {
1817 /* convert first to a multi */
1818 multi = constant_pmc_new(interpreter, enum_class_MultiSub);
1819 VTABLE_add_method(interpreter, class, meth_name, multi);
1820 VTABLE_push_pmc(interpreter, multi, method);
1822 else {
1823 assert(method->vtable->base_type == enum_class_MultiSub);
1824 multi = method;
1826 method = constant_pmc_new(interpreter, enum_class_NCI);
1827 VTABLE_set_pointer_keyed_str(interpreter, method,
1828 const_string(interpreter, signature),
1829 F2DPTR(func_ptr));
1830 VTABLE_push_pmc(interpreter, multi, method);
1832 /* mark MMD */
1833 PObj_get_FLAGS(method) |= PObj_private0_FLAG;
1835 * attach the multi_signature array to PMC_pmc_val
1837 multi_sig = constant_pmc_new(interpreter, enum_class_FixedIntegerArray);
1838 VTABLE_set_integer_native(interpreter, multi_sig, 2);
1839 VTABLE_set_integer_keyed_int(interpreter, multi_sig, 0, type);
1840 VTABLE_set_integer_keyed_int(interpreter, multi_sig, 1, right);
1841 PMC_pmc_val(method) = multi_sig;
1844 * push method onto core multi_sub
1845 * TODO cache the namespace
1847 multi = Parrot_find_global_n(interpreter, ns,
1848 const_string(interpreter, short_name));
1849 assert(multi);
1850 VTABLE_push_pmc(interpreter, multi, method);
1853 static void
1854 mmd_create_builtin_multi_meth(Interp *interpreter, PMC *ns, INTVAL type,
1855 const MMD_init *entry)
1857 mmd_create_builtin_multi_meth_2(interpreter, ns,
1858 entry->func_nr, type, entry->right, entry->func_ptr);
1863 =item C<void Parrot_mmd_register_table(Interp*, INTVAL type,
1864 MMD_init *, INTVAL)>
1866 Register MMD functions for this PMC type.
1868 =cut
1873 void
1874 Parrot_mmd_register_table(Interp* interpreter, INTVAL type,
1875 const MMD_init *mmd_table, INTVAL n)
1877 INTVAL i;
1878 MMD_table *table;
1879 PMC *ns;
1881 table = interpreter->binop_mmd_funcs;
1882 ns = mmd_make_ns(interpreter);
1883 if ((INTVAL)table->x < type && type < enum_class_core_max) {
1885 * pre-allocate the function table
1887 for (i = 0; i < MMD_USER_FIRST; ++i) {
1888 mmd_register(interpreter, i, enum_class_core_max - 1,
1889 enum_class_core_max - 1, NULL);
1891 * create a MultiSub stub
1893 mmd_create_builtin_multi_stub(interpreter, ns, i);
1897 * register default mmds for this type
1899 for (i = 0; i < n; ++i) {
1900 assert((PTR2UINTVAL(mmd_table[i].func_ptr) & 3) == 0);
1901 mmd_register(interpreter,
1902 mmd_table[i].func_nr, type,
1903 mmd_table[i].right, mmd_table[i].func_ptr);
1904 mmd_create_builtin_multi_meth(interpreter, ns, type, mmd_table + i);
1911 =item C<void Parrot_mmd_rebuild_table(Interp*, INTVAL type, INTVAL func_nr)>
1913 Rebuild the static MMD_table for the given class type and MMD function
1914 number. If C<type> is negative all classes are rebuilt. If C<func_nr> is
1915 negative all MMD functions are rebuilt.
1917 =cut
1921 void
1922 Parrot_mmd_rebuild_table(Interp* interpreter, INTVAL type, INTVAL func_nr)
1924 MMD_table *table;
1925 UINTVAL i;
1927 if (!interpreter->binop_mmd_funcs)
1928 return;
1929 table = interpreter->binop_mmd_funcs + func_nr;
1930 if (!table)
1931 return;
1933 /* TODO specific parts of table
1934 * the type and it's mro and
1935 * all classes that inherit from type
1937 for (i = 0; i < table->x * table->y; ++i)
1938 table->mmd_funcs[i] = NULL;
1943 =back
1945 =head1 SEE ALSO
1947 F<include/parrot/mmd.h>,
1948 F<http://svn.perl.org/perl6/doc/trunk/design/apo/A12.pod>,
1949 F<http://svn.perl.org/perl6/doc/trunk/design/syn/S12.pod>
1951 =cut
1957 * Local variables:
1958 * c-file-style: "parrot"
1959 * End:
1960 * vim: expandtab shiftwidth=4: