Возможно, вы захотите взглянуть на VMMaker . Его класс Interpreter - это парень, который выполняет байт-коды CompiledMethod и фактически отправляет сообщения вашим объектам.
Например, если вы посмотрите на байт-коды для Object >> RespondsTo: вы увидите
17 <70> self
18 <C7> send: class
19 <10> pushTemp: 0
20 <E0> send: canUnderstand:
21 <7C> returnTop
Интерпретатор читает байт-код, ищет этот байт-код в его BytecodeTable (инициализированном в классе интерпретатора >> initialiseBytecodeTable) и выполняет соответствующий метод. Поэтому <70> (#pushReceiverByteCode) помещает себя во внутренний стек интерпретатора. Затем (#bytecodePrimClass) сводится к «найти класс себя». <10> (#pushTeilitaryVariableBytecode) помещает аргумент в #respondsTo: в стек. Интересная часть происходит с (#sendLiteralSelectorBytecode), который вызывает self normalSend
. #normalSend, в свою очередь, определяет класс получателя (в данном случае self class
), а затем вызывает self commonSend
, который находит фактический метод, который мы стремимся запустить, и затем запускает его.
Я новичок в ВМ; вышеприведенное может быть не лучшим местом, где можно увидеть алгоритм в действии и т. д. (или даже лучшим объяснением), но я надеюсь, что это хорошее место для начала.
Алгоритм, используемый виртуальной машиной для фактической отправки сообщения, описан в вашем вопросе. Фактическая реализация этого алгоритма определена в Interpreter>>commonSend
. Алгоритм поиска в Interpreter>>lookupMethodInClass:
и алгоритм выполнения в Interpreter>>internalExecuteNewMethod
.
Первое работает так же, как вы описываете:
- Элемент списка
- Попробуйте найти метод в этом классе.
- Если не найдено, посмотрите в суперклассе.
- Если это не удается, попробуйте найти #doesNotUnderstand:
- Если #doesNotUnderstand: нигде в иерархии классов не существует, выведите ошибку.
Последний работает так:
- Если это примитив, запустите примитив.
- Если это не так, активируйте новый метод (создайте новую запись активации).
- (Проверьте на наличие прерываний.)