> > 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