Redesign the undefined function handler API
Created by: wks
Current status
Currently there are two handlers: trap handler and undefined function handler. They are different.
In trap handlers:
- Thread is temporarily unbound from the stack, and is rebound after trap handling.
- Stack is unbound and is ready for introspection.
- OSR (pop frame, push frame) is possible.
In undefined function handlers:
- Thread is, conceptually, still executing the CALL instruction that calls the undefined function.
- The stack is in the RUNNING state, not available for introspection or OSR
- The client must define the function. Then CALL will be tried again. i.e. If the client does not define the function, it will loop forever.
This asymmetry is complicating the interface.
- The undefined function handling API leaves no room for errors in loading the bundle. If, in Java, the client puts a stub for an unloaded method, and there is an error when loading the class, then the client has no other things to do than "defining" the method as "doing nothing but throwing an exception". This isn't very nice.
- It leaves the stack and the thread in a strange state: they cannot continue, but still "RUNNING".
- Introspection and OSR are impossible.
Proposed change
I propose unifying the two interfaces.
Let undefined functions behave like:
.funcdef @undefined_function VERSION noversion <sig> (a1 a2 a3 ...) {
%entry:
TRAP <void> KEEPALIVE (a1 a2 a3...)
undefined behaviour after TRAP
}
That is, an undefined function behaves like executing a trap with all parameters as keep-alive variables.
Add an extra API call:
MuID (*cur_func)(MuCtx *ctx, MuStackRefValue stack, int frame)
It returns the ID of the function of the current frame. It works even if the function is not defined. Return 0 if the frame is native.
Modify the cur_func_ver
API call: It returns 0 when the frame is a frame without version (undefined function).
So a client can identify an undefined function by "cur_func != 0 && cur_func_ver == 0". FYI:
-
cur_func != 0 && cur_func_ver != 0
: a defined Mu function -
cur_func != 0 && cur_func_ver == 0
: an undefiend Mu function -
cur_func == 0 && cur_func_ver == 0
: native frame -
cur_func == 0 && cur_func_ver != 0
: impossible
Modify the cur_inst
API call: It returns 0 if the frame is just created (before the first instruction), is native, or if the function is not defined.
Modify the dump_keepalives
API call: It dumps keep-alive variables for OSR point instructions, or the arguments to an undefined function. Cannot be used on native frames or a just-created frame.
The pop_frame
and the push_frame
API behave like before. Specifically to undefined functions, popping an undefined function frame reveals its caller to the top of the stack. After loading a bundle that defines the function, a push_frame
can re-creates the callee frame, but this time the callee is defined. The client can also choose not to rebind to the same stack, but swap away. Or just throw an exception (probably ClassNotFoundException
) to the caller without defining the callee.
About the NEWSTACK instruction
Creation of the stack will be successful, but it will trap when executing the undefined function after binding the stack to a thread.