src/mmd.c - Multimethod dispatch for binary opcode functions


This system is set up to handle type-based dispatching for binary (i.e. two-arg) functions. This includes, though isn't necessarily limited to, binary operators such as addition or subtraction.


The MMD system is straightforward, and currently must be explicitly invoked, for example by a vtable function. (We may reserve the right to use MMD in all circumstances, but currently do not).


For the purposes of the API, each MMD-able function is assigned a unique number which is used to find the correct function table. This is the func_num parameter in the following functions. While Parrot isn't restricted to a predefined set of functions, it does set things up so that all the binary vtable functions have a MMD table preinstalled for them, with default behaviour.

Remarks ^

binop_mmd_funcs->x and ->y are table sizes not highest type in table.

Functions ^

void mmd_dispatch_v_ppp(Interp *interpreter, PMC *left, PMC *right, PMC *dest, INTVAL function)

Dispatch to a multimethod that "returns" a PMC. left, right, and dest are all PMC pointers, while func_num is the MMD table that should be used to do the dispatching.

The MMD system will figure out which function should be called based on the types of left and right and call it, passing in left, right, and dest like any other binary vtable function.

This function has a void return type, like all the "take two PMCs, return a PMC" vtable functions do.

void mmd_dispatch_v_pip(Interp *interpreter, PMC *left, INTVAL right, PMC *dest, INTVAL function)

Like above, right argument is a native INTVAL.

void mmd_dispatch_v_pnp(Interp *interpreter, PMC *left, FLOATVAL right, PMC *dest, INTVAL function)

Like above, right argument is a native FLOATVAL.

void mmd_dispatch_v_psp(Interp *interpreter, PMC *left, STRING *right, PMC *dest, INTVAL function)

Like above, right argument is a native STRING *.

INTVAL mmd_dispatch_i_pp(Interp *interpreter, PMC *left, PMC *right, INTVAL function)

Like mmd_dispatch_v_ppp(), only it returns an INTVAL.

void mmd_add_function(Interp *interpreter, INTVAL funcnum, funcptr_t function)

Add a new binary MMD function to the list of functions the MMD system knows of. func_num is the number of the new function, while default_func is the function to be called when the system doesn't know which function it should call. (Because, for example, there hasn't been a function installed that matches the left and right types for a call).

static void mmd_expand_x(Interp *interpreter, INTVAL function, INTVAL new_x)

Expands the function table in the X dimension to include new_x.

static void mmd_expand_y(Interp *interpreter, INTVAL function, INTVAL new_y)

Expands the function table in the Y direction.

void mmd_add_by_class(Interp *interpreter, INTVAL functype, STRING *left_class, STRING *right_class, funcptr_t funcptr)

Add a function to the MMD table by class name, rather than class number. Handles the case where the named class isn't loaded yet.

Adds a new MMD function funcptr to the func_num function table that will be invoked when the left parameter is of class left_class and the right parameter is of class right_class. Both classes are STRING *s that hold the PMC class names for the left and right sides. If either class isn't yet loaded, Parrot will cache the information such that the function will be installed if at some point in the future both classes are available.

Currently this is done by just assigning class numbers to the classes, which the classes will pick up and use if they're later loaded, but we may later put the functions into a deferred table that we scan when PMC classes are loaded. Either way, the function will be guaranteed to be installed when it's needed.

The function table must exist, but if it is too small, it will automatically be expanded.

void mmd_register(Interp *interpreter, INTVAL type, INTVAL left_type, INTVAL right_type, funcptr_t funcptr)

Register a function funcptr for MMD function table func_num for classes left_type and right_type. The left and right types are INTVALs that represent the class ID numbers.

The function table must exist, but if it is too small, it will automatically be expanded.

Adding a new function to the table can be interestingly non-trivial, so we get to be tricky.

If the left or right types are larger than anything we've seen so far, it means that we have to expand the table. Making Y larger is simple -- just realloc with some more rows. Making X larger is less simple. In either case, we punt to other functions.

TODO - Currently the MMD system doesn't handle inheritance and best match searching, as it assumes that all PMC types have no parent type. This can be considered a bug, and will be resolved at some point in the future.

void mmd_destroy(Parrot_Interp interpreter)

Frees all the memory allocated used the MMD subsystem.

PMC *mmd_vtfind(Parrot_Interp interpreter, INTVAL type, INTVAL left, INTVAL right)

Return an MMD PMC function for the given data types. The return result is either a Sub PMC (for PASM MMD functions) or a CSub PMC holding the C function pointer in PMC_struct_val. This CSub is not invocable, you have to wrap it into an NCI function to get the required function arguments passed.