VTABLE invoke

Whiteknight on 2009-01-29T00:23:21

kjs put in a great little commit the other day that resolved an inconsisency in the way VTABLE overrides were handled in PIR.

When you define a class in PIR, you get the ability to override the default VTABLE interfaces for that class. These overrides should, in theory, have access to all the same facilities that their C-written breathren have. One of these features is the "self" keyword, a magical variable that contains a reference to the PMC object that the VTABLE override is being invoked on. Before kjs' commit, the "self" parameter was only passed in some situations. Now, it's always passed.

For a large number of reasons, this causes a failure in the handling of the "invoke" VTABLE method, which is a special case. People want to be able to invoke a PMC class like this:

$P0()

However, this raises a number of problems: We don't know what type of object $P0 is until runtime, so IMCC (or PIRC) cannot know to generate an extra instruction to pass an extra invocant parameter at compile-time. With kjs' patch, calling the statement above causes an error in the overridden invoke VTABLE: one parameter is expected ("self"), but none have been passed.

There are two ways to get around this currently:

$P0($P0) # pass "self" explicitly $P0.$P0() # IMCC thinks it's a :method

I was thinking that either of these could be easily macroized away behind a compiler directive:
.pmcinvoke($P0 [,args]) # $P0.$P0(args)

or:
.pmcinvoke($P0 [,args]) # $P0($P0, args)

However, this is messy too because every time we want to invoke a custom object we need to write all this nonsense. Remember, what we want to be able to write is this:

$P0()

Instead of changing things on the caller's side, which we know we can't really do, we can consider changing things at the callees side instead. As things are, the invocant is passed like a normal parameter, except at the front of the list. What if, instead of passing it implicitly at the beginning of the list, we pass it through some other internal mechanism? In fact, we have an opcode that can do just that:

$P0 = interpinfo .INTERPINFO_CURRENT_OBJECT

if IMCC generated that op at the beginning of every :vtable override, we don't need to worry about passing the invocant and then VTABLE_invoke will just work.

This is maybe even more heavyweight then we need. If "self" in PIR were just an alias for the interpreter field "interp->current_object" (maybe a read-only alias for it) it could just exist everywhere (and would only be non-null in :method and :vtable subroutines)

In any case, this is a complicated situation and there are no easy resolutions in sight. We may just have to stick with the "$P0.$P0()" syntax that kjs suggests. It's not terrible, but I do think it can be better.