Ninkasi Bugs 2
Fixed one more Ninkasi bug today!
Functions in Ninkasi have three undocumented variables. Named function arguments just refer to stack positions relative to the current stack frame, so it was pretty simple to add variables that refer to the function ID itself, the return pointer, and the argument count, because all of these values exist on the stack just like the arguments to the function itself.
These are considered debugging-only features, but they should at least be not-broken debugging-only features.
These variables are:
-
_functionId
- The function being called. This can be called as a function, itself. There are a few things to watch out for with it, however. If used with callable objects, the value will be the object and not the function. This is because the 'call' instruction accepts that object as its function argument and then alters the argument list and argument count. -
_argumentCount
- The number of arguments. This will include the object's_data
field when usedwith a callable object, which will be different from the actual argument count from the call. This is less useful because Ninkasi does not (yet) support varargs function calls for functions defined in the scripting system though it someday could. Altering this will likely cause stack corruption. -
_returnPointer
- The instruction pointer to return to when the "return" instruction executes. Not super useful from a scripting standpoint, unless you want some insight about the caller. If you want to live dangerously, you can even alter it before returning! (But I don't recommend it. Will also cause stack corruption, probably.)
The Bug
At some point along the way, I had designed the 'call' instruction to expect a stack that looked like this (example 3-parameter function):
index | value | type
---------------------------------
-6 | ... |
-5 | arg0 | any type
-4 | arg1 | any type
-3 | arg2 | any type
-2 | _functionId | function
-1 | _argumentCount | integer
Later down the road, I had to change it to this:
index | value | type
---------------------------------
-6 | ... |
-5 | _functionId | function
-4 | arg0 | any type
-3 | arg1 | any type
-2 | arg2 | any type
-1 | _argumentCount | integer
I'm not sure off the top of my head why I had to do this, but my guess is that it was simply the order that arguments needed to be evaluated and then pushed onto the stack.
Anyway, when I made this change, I apparently forgot to update the
position of _functionId
. The arguments still needed a +1 offset in
the stack that I always considered a sort of mystery. Well, the +1
offset was to skip the _functionId
, and the functionId
was
pointing to one of the arguments.
The Fix
This was a pretty simple fix. Just remove the offset, and move the
_functionId
setup code to before the argument setup loop.
Sometimes it really is that easy!