When sending a message to an object开发者_如何学C in Squeak, the runtime invocation algorithm is something like
- curr <- the receiver's class
- Repeat while curr isn't nil
- Search for the selector in that class's methods; if it's there, invoke it and return
- curr <- curr's superclass
- Call
doesNotUnderstand:
onself
Now, a very similar algorithm is used for the respondsTo:
method, and indeed it can be seen by inspecting respondsTo:
's code. What I'm trying to find is the location of the code for the above algorithm used for invocation.
I know perform:
does something similar but I believe it's not used for regular method invocation but only as a reflection-like method calling mechanism (e.g. when the method name is not known to the programmer until the runtime).
If the code above is also hidden as a primitive directive, where would I find the primitive call? If it isn't, where would I find the code itself?
You'd probably want to look at VMMaker. Its Interpreter class is the guy that executes a CompiledMethod's bytecodes, and will actually send the messages to your objects.
For instance, if you look at the bytecodes for Object>>respondsTo: you'll see
17 <70> self
18 <C7> send: class
19 <10> pushTemp: 0
20 <E0> send: canUnderstand:
21 <7C> returnTop
The Interpreter reads in a bytecode, looks up that bytecode in its BytecodeTable (initialised in Interpreter class>>initialiseBytecodeTable) and executes the appropriate method. So <70> (#pushReceiverByteCode) pushes self onto the Interpreter's internal stack. Then (#bytecodePrimClass) boils down to "find self's class". <10> (#pushTemporaryVariableBytecode) pushes the argument to #respondsTo: onto the stack. The interesting part happens with (#sendLiteralSelectorBytecode), which calls self normalSend
. #normalSend in turn figures out the class of the receiver (self class
in this case), and then calls self commonSend
, which finds the actual method we seek to run, and then runs it.
I'm a VM newbie; the above might not be the absolute best place to see the algorithm in action, etc., (or even the best explanation) but I hope it's a good place to start.
The algorithm used by the VM to actually send a message is as you outline in your question. The actual implementation of that algorithm's defined in Interpreter>>commonSend
. The lookup algorithm's in Interpreter>>lookupMethodInClass:
and the execution algorithm's in Interpreter>>internalExecuteNewMethod
.
The former works much as you describe:
- List item
- Try find the method in this class.
- If not found, look in the superclass.
- If this recursively fails, try find #doesNotUnderstand:
- If #doesNotUnderstand: doesn't exist anywhere in the class hierarchy, throw an error.
The latter works like this:
- If it's a primitive, run the primitive.
- If it's not, activate the new method (create a new activation record).
- (Check for interrupts.)
Digging some more, the ContextPart
class is an interpreter capable of running bytecode. According to its documentation:
[its methods relevant to this question] exactly parallel the operation of the Smalltalk machine itself.
If we check how it interprets bytecode,
- Its
interpret
method calls itsinterpretNextInstructionFor:
for each instruction. interpretNextInstructionFor:
callssend:super:numArgs:
when a send instruction is encountered.send:super:numArgs:
callssend:to:with:super:
(assuming it is not a primitive message).send:to:with:super:
usesBehavior
'slookupSelector:
to locate the correct selector to use.Behavior
'slookupSelector:
is the one responsible for the superclass loop in the algorithm appearing in the question.
So this is not the actual implementation I was looking for (and thus this is not really an answer), but I guess that it can help with understanding the nuances of the precise algorithm.
To understand Franks response, you need some background info:
the compiler generates a "send bytecode", which is later executed by the VM's bytecode interpreter (or jitted, but the semantics is the same). Thus, you would not expect to find the implementation in any class, but in the VM.
Most other VM's are written in C, Assembler or what else...
However: the squeak VM is written in Smalltalk, and compiled to C by a "Subset-of-Smalltalk-to-C-Compiler" (so called "Slang", because it does not cover the full Smalltalk semantics).
Being written in Smalltalk, that VM can of course be developed, debugged and tested from within Squeak (by running the Slang-interpreter on the image from within the image). That is why you can find the implementation in Interpreter as described by Frank.
精彩评论