So just run an image with the simulator and stop at any of commonSend, internalFindNewMethod, lookupMethodInClass: and you'll see the call chain form the relevant send bytecode.
Here's an expression that loads and runs an image with the StackInterpreter:
cool I will see if I can show that during my lecture.
| vm | StackInterpreter initializeWithOptions: (Dictionary newFromPairs: #()). vm := StackInterpreterSimulator new. vm openOn: '/Users/eliot/Squeak/Squeak4.4/trunk44.image'. vm openAsMorph; run
and if you break on entry to internalFindNewMethod you'll see this stack:
StackInterpreterSimulatorLSB(Object)>>break StackInterpreterSimulatorLSB(StackInterpreter)>>internalFindNewMethod StackInterpreterSimulatorLSB(StackInterpreterSimulator)>>internalFindNewMethod StackInterpreterSimulatorLSB(StackInterpreter)>>commonSend StackInterpreterSimulatorLSB(StackInterpreterSimulator)>>commonSend StackInterpreterSimulatorLSB(StackInterpreter)>>normalSend StackInterpreterSimulatorLSB(StackInterpreter)>>secondExtendedSendBytecode StackInterpreterSimulatorLSB(StackInterpreterSimulator)>>dispatchOn:in: StackInterpreterSimulatorLSB(StackInterpreterSimulator)>>run UndefinedObject>>DoIt
If you find out what the selector is (e.g. self printOop: messageSelector) you can then use the simulator;s own breakpoint facilities:
| vm | StackInterpreter initializeWithOptions: (Dictionary newFromPairs: #()). vm := StackInterpreterSimulator new. vm openOn: '/Users/eliot/Squeak/Squeak4.4/trunk44.image'. vm setBreakSelector: #&. vm openAsMorph; run
and now it'll stop in sendBreak:point:receiver: and you'll get this stack:
StackInterpreterSimulatorLSB(Object)>>halt: StackInterpreterSimulatorLSB(StackInterpreterSimulator)>>sendBreak:point:receiver: StackInterpreterSimulatorLSB(StackInterpreter)>>commonSend StackInterpreterSimulatorLSB(StackInterpreterSimulator)>>commonSend StackInterpreterSimulatorLSB(StackInterpreter)>>normalSend StackInterpreterSimulatorLSB(StackInterpreter)>>secondExtendedSendBytecode StackInterpreterSimulatorLSB(StackInterpreterSimulator)>>dispatchOn:in: StackInterpreterSimulatorLSB(StackInterpreterSimulator)>>run UndefinedObject>>DoIt
- what is the invariant? that currentclass always point to the currently looked up class and newMethod is the found method?
The input arguments are messageSelector, argumentCount and its argument, class. The VM suffers somewhat from being cluttered with global variables, which is I think a legacy of the blue-book specification (the Smalltalk code that was used to implement Interpreter in VMMaker) being derived from the Alto and Dorado implementations in microcode. It would be nicer if there were fewer variables, but e.g. supplying argumentCount as a parameter throughout all the message handling routines can be difficult, especially when things like perform: and MNU change the argumentCount during message send.
I clearly see what you mean.
- why lookupMethodFor: selector InDictionary: dictionary is defined in StackInterpreter but never used only in CoInterpreter?
Well, it belongs in StackInterpreter and could be used in the other lookup methods. I was probably considering rewriting the other lookup routines to use lookupMethodFor: InDictionary: but didn't get round to it.
ok I see.
While reading directly the see code I was wondering why you generate the ifdef for different bytecode set? Is it that you would have to call another object and that it would not have the same instance variables. Because I was thinking that such variation points could be handled by the different interpreter subclasses.
Bytecode dispatch is performance-critical in the context and stack interpreters. The ifdef means that if one is not using multiple bytecode sets dispatch is simpler and hence faster.
Subclassing is very difficult because the simulators are not traits and so subclass each interpreter class. hence adding a subclass to handle this difference means adding another simulator, and they're big and tedious to maintain. IMO the multiple bytecode set work just isn't worth that kind of load. The VM would be nicer if more fully decomposed. I've done a fair amount (moving the objectMemory into an instance variable, putting the Cogit in its own class hierarchy, adding support for struct classes that define data types like a stack page or an abstract instruction or machine-code method in the JIT) but there's always more that one can do.
cheers!
Thanks
lookupMethodInClass: class | currentClass dictionary found | <inline: false> self assert: class ~= objectMemory nilObject. currentClass := class. [currentClass ~= objectMemory nilObject] whileTrue: [dictionary := objectMemory fetchPointer: MethodDictionaryIndex ofObject: currentClass.
*** trick with the cannotInterpret *** dictionary = objectMemory nilObject ifTrue: ["MethodDict pointer is nil (hopefully due a swapped out stub) -- raise exception #cannotInterpret:." self createActualMessageTo: class. messageSelector := objectMemory splObj: SelectorCannotInterpret. self sendBreak: messageSelector + BaseHeaderSize point: (objectMemory lengthOf: messageSelector) receiver: nil. ^self lookupMethodInClass: (self superclassOf: currentClass)]. *** trick with the cannotInterpret end ***
found := self lookupMethodInDictionary: dictionary. found ifTrue: [^currentClass]. ^^^^^^^^^^^^^^^^^^^^^^^^^
currentClass := self superclassOf: currentClass].
"Could not find #doesNotUnderstand: -- unrecoverable error." messageSelector = (objectMemory splObj: SelectorDoesNotUnderstand) ifTrue: [self error: 'Recursive not understood error encountered'].
"Cound not find a normal message -- raise exception #doesNotUnderstand:" self createActualMessageTo: class. messageSelector := objectMemory splObj: SelectorDoesNotUnderstand. self sendBreak: messageSelector + BaseHeaderSize point: (objectMemory lengthOf: messageSelector) receiver: nil. ^self lookupMethodInClass: class
if (found) { return currentClass; }
static sqInt lookupMethodInClass(sqInt class) { // StackInterpreter>>#lookupMethodInClass: DECL_MAYBE_SQ_GLOBAL_STRUCT sqInt currentClass; sqInt dictionary; sqInt found; sqInt header; sqInt index; sqInt length; sqInt mask; sqInt methodArray; sqInt nextSelector; sqInt sz; sqInt wrapAround;
assert(class != (nilObject())); currentClass = class; while (currentClass != GIV(nilObj)) { dictionary = longAt((currentClass + BaseHeaderSize) + (MethodDictionaryIndex << ShiftForWord)); if (dictionary == GIV(nilObj)) {
/* ifTrue: */
createActualMessageTo(class); GIV(messageSelector) = longAt((GIV(specialObjectsOop) + BaseHeaderSize) + (SelectorCannotInterpret << ShiftForWord)); sendBreakpointreceiver(GIV(messageSelector) + BaseHeaderSize, lengthOf(GIV(messageSelector)), null); return lookupMethodInClass(longAt((currentClass + BaseHeaderSize) + (SuperclassIndex << ShiftForWord))); } /* begin lookupMethodInDictionary: */ /* begin fetchWordLengthOf: */ /* begin sizeBitsOf: */ header = longAt(dictionary); sz = ((header & TypeMask) == HeaderTypeSizeAndClass ? (longAt(dictionary - (BytesPerWord * 2))) & LongSizeMask : header & SizeMask); length = ((usqInt) (sz - BaseHeaderSize)) >> ShiftForWord; mask = (length - SelectorStart) - 1;
/* messageSelector */
index = SelectorStart + (mask & (((GIV(messageSelector) & 1) ? (GIV(messageSelector) >> 1) : (((usqInt) (longAt(GIV(messageSelector)))) >> HashBitsOffset) & HashMaskUnshifted))); wrapAround = 0; while (1) { nextSelector = longAt((dictionary + BaseHeaderSize) + (index << ShiftForWord)); if (nextSelector == GIV(nilObj)) { found = 0; goto l1; } if (nextSelector == GIV(messageSelector)) { methodArray = longAt((dictionary + BaseHeaderSize) + (MethodArrayIndex << ShiftForWord)); GIV(newMethod) = longAt((methodArray + BaseHeaderSize) + ((index - SelectorStart) << ShiftForWord)); found = 1; goto l1; } index += 1; if (index == length) { if (wrapAround) { found = 0; goto l1; } wrapAround = 1; index = SelectorStart; } } found = 0; l1: /* end lookupMethodInDictionary: */; if (found) { return currentClass; } currentClass = longAt((currentClass + BaseHeaderSize) + (SuperclassIndex << ShiftForWord)); } if (GIV(messageSelector) == (longAt((GIV(specialObjectsOop) + BaseHeaderSize) + (SelectorDoesNotUnderstand << ShiftForWord)))) { error("Recursive not understood error encountered"); } createActualMessageTo(class); GIV(messageSelector) = longAt((GIV(specialObjectsOop) + BaseHeaderSize) + (SelectorDoesNotUnderstand << ShiftForWord)); sendBreakpointreceiver(GIV(messageSelector) + BaseHeaderSize, lengthOf(GIV(messageSelector)), null); return lookupMethodInClass(class); }
-- best, Eliot
-- best, Eliot