So I now have the beginning of a type system (need to write about this) and a garbage collector for Orbit, which means I need to start thinking about how bytecode actually runs.
I want to have system close to java’s, where the machine takes in compiled object files that contain the bytecode for a script, along with the constant pool and user-defined type. That’s step 1.
Where it gets a bit less clear to me is what happens when I want to start
running the newly loaded module (let’s call it a module). Ideally, it’s a call
to orbit_run(OrbitVM* vm, VMModule* module, const char* signature)
. So in
order to have that, I have to do a few things.
-
Resolve the function that matches
signature
in the module. -
Create a new object that represents this execution. Let’s call it
VMTask
. Each task will probably need its own operand stack and call frames stack. -
Push a frame in the task’s call stack that points to the start function.
-
Start running bytecode, pushing more functions as we go, until the call frame stack is empty.
For the moment, I’m fairly happy with this. Global variables live in their
module, and as long as I have a way to save a module from being garbage
collected in between tasks, I can reuse it multiple times – say, once per game
loop if it were used for game engine scripting. Plus, VMTask
could easily
become a co-routine or thread system.