The Activation Record
Until all of the (WHY???)'s or "(??"'s are gone, this document
Ok, as seen from an intstruction in a code segment, here
is the stack..
x Arg x
First bits of code within a method:
Keep the previous info in mind.
(Need to finish implementing, and use 'stackframe.doc' as the working template)
This section describes the most complicated portion of this system. Understanding it is necessary for working on the runtime.
In the following paragraphs, I will state that code can be activated, this is the same as saying 'run'.
Code vs. Blocks
When a proto has code defined for the slot,
the slot is initialized with a reference to a code proto. The named slot
is now what is called a 'PassThru' to the code proto. This is what is the
default case when you compile code. The code proto has a slot called 'value'
or 'value:' or 'value:with:', etc.
When a slot refers to a code proto and it wasn't setup as a passThru, then it is a normal reference. The only way to activate it is to use the 'value', or 'value:", etc., message. (Which is what the PassThru refers to)
Lets assume that we are already in a method and we are in some code which is about to activate a slot.
The first thing the compiler will do is push the arguments onto the stack. They will be pushed in reverse order (like C). The first argument will be pushed last (which should be the target of the slot activation).
Special. If any of the arguments
are references to blocks, then the VM must clone the proto and set the
parent to refer to this context during these push statements. It does that
by making the parent of the newly cloned block proto a Context Reference.
This reference should be the FP of the context on the stack of this activation...
the one doing the push.
After the arguments, an argument count should be pushed on the stack. This is useful for simple error checking and/or for getting information about the activation later on. For example, later on we may have the ability to treat the entire argument as an array and access it or manipulate it.
After that, the call to activateSlot is done. The VM will do several things automatically. The first thing that it will do is increment the program counter to be behind the operand of the call (so that when the return happens, the code executes from there). Next the VM pushes the program counter onto the stack. Finally, the function is called.
For the purposes of this discussion, the call is always to 'activateSlot'.
activateSlot is a very, very, very powerful function (it is also slightly complex). It embodies the core principles of the prototype inheritance system. The first thing it will do is locate the slot in the target. If the slot isn't there, then it will lookup the parent slot of the target. If it finds the parent, then it will follow the parent slot and start the search again at that proto. If that slot doesn't exist, the algorithm will lookup the information in the DefaultBehaviours or the Global namespaces. If it does find the slot, then it will go to the next step. If it finds it and it was a GETFRAME or Frame referenced parent variable, it will use that value. Finally, if a method isn't found or it goes up too many parent levels (32 currently), then it declares the activateSlot a 'method not found' error. This should then be considered an 'exception' (which will be handled by an exception handling system in the near future.)
Special. Code protos will usually
have their parent slot set as a GETFRAME type. This is because code protos
refer to their local variables (which have a higher priority than the target
object). So, the code-proto's parent will refer to the actual target that
was pushed onto the stack. Usually this code-proto is the 'secret self'.
OK, once it finds the slot, it peforms
the specific action. The actions are GET, SET, GETFRAME, SETFRAME, PASSTHRU,
CODE, or INTRINSIC.
PASSTHRU will use the data as a reference to a code proto and activate the method (via slot '_code_'). Note: code protos only have one '_code_' slot... a code proto exists for each method. The result of the PASSTHRU is a new context... more on that later. CODE activations are the results of 'do or value' or 'value:' slots. If you are referencing a code proto directly (as a block), then this can happen. The result of activating this method will cause a new context. INTRINSIC slots will cause the referenced C code to be activated. The slot values are set to pointers to routines that are written in C or assembly.
For all of those actions, only two are important. The PASSTHRU and CODE slots. They take us to the next parts of the activation (because all of the others will do their work and leave the result on the stack frame... ie. these cause new activations)
Next Part of the Activation
These two action handlers will do the next interesting thing.
Generally, the target of a call will be some proto or the code-proto for the current context (secret self). The first activateSlot in a method will always have the code-proto as the first call. After that, a new target proto may be acquired and it will be the target for subsequent calls to activateSlot. So, to reiterate, the target of a call could be the code-proto or it could be some other object. The reason that this is important is as follows:
When a new activation is started, and it does the 'frame mapped parent local', and it just used the target, it would be using the previous context's code proto. This is not what is intended.
So, they will look at that target and judge
by it's type (which is encoded in the reference) to see if it is a code-proto
or otherwise (A relatively inexpensive check.). If it is a code-proto,
it will take the code-proto's parent from the secret self as the parent-local
from the existing context (since the FP didn't change). If it isn't a code-proto,
it will just take the target. Once it has this value it will push it onto
We are now ready to proceed to the next step.
Finally, the handler will return. This
will cause the VM to continue executing code.
At this point, the code can now execute.
Everything that needed to be done is done. The first set of code should
be for local variable initialization.
Returning within the Method
Special. Lets review and add to
the handling of the 'parent' slot for a block activation.
Once it finds the slot, the typical handlers
will take care of the call (using this hidden FP). However, if it is a
passThru or code it will work. Just as it does normally (the target and
the new method proto will be mapped, and a new context is started. They
will not need the hidden FP). The moral of this story is that the FP doesn't
get changed in a visible way.
Worst Case Example
A worst case scenario would be a method
activation after a block was activated, and that was just described in
the previous paragraph.
6/27/99 - Changed the activation documentation,
it is too hazy...