Smalltalk:原语是如何实现的?
Smalltalk: How primitives are implemented?
我知道一切皆对象,在 Smalltalk 中,您可以向对象发送消息来完成几乎所有事情。
现在我们如何实现一个对象(内存表示和基本操作)来表示原始数据类型?例如,整数的 +
是如何实现的?
我查看了 Smalltalk 的源代码并在 Smallint.st
中找到了这个。有人可以解释这段代码吗?
+ arg [
"Sum the receiver and arg and answer another Number"
<category: 'built ins'>
<primitive: VMpr_SmallInteger_plus>
^self generality == arg generality
ifFalse: [self retrySumCoercing: arg]
ifTrue: [(LargeInteger fromInteger: self) + (LargeInteger fromInteger: arg)]
]
这里是上面代码的 link: https://github.com/gnu-smalltalk/smalltalk/blob/62dab58e5231909c7286f1e61e26c9f503b2b3df/kernel/SmallInt.st
从概念上讲,原始 方法是由虚拟机 (VM) 而非常规 Smalltalk 代码实现的行为片段(例程)。
当 Smalltalk 编译器找到语句 <primitive: ...>
时,它会将此解释为一种特殊类型的方法,其参数(在您的例子中为 VMpr_SmallInteger_plus
)表示目标例程在 VM 中的整数索引。
从这个意义上说,原语是一个全局例程,不绑定到任何特定 class 的 MethodDictionary
。原始逻辑适用于某些 classes 的接收者和参数,这就是为什么它必须检查接收者和参数(如果有)是否符合其要求。如果不是,原语 失败 并且在这种情况下,控制流向 <primitive: ...>
语句之后的 Smalltalk 代码。否则原语 成功 并且下面的 Smalltalk 代码不会被执行。另请注意,编译器不允许在 <primitive:...>
句子上方出现临时声明以外的任何 Smalltalk 代码。
在您的示例中,如果参数 arg
不是预期的 class(大概是 SmallInteger
),例程将放弃尝试将其求和给接收者并委托将操作解析为 Smalltalk 代码。
如果参数恰好是 SmallInteger
,原语将计算结果(使用 VM 中保存的例程)并用它回答。
我还没有看到这个原语的代码,但是如果总和的结果不适合SmallInteger
,原语也可能会失败,在这种情况下,接收者和参数都会失败将转换为 LargeInteger
s 并且添加将在适当的 class(LargePositiveInteger
或 LargeNegativeInteger
)的 #+
方法中进行。
Smalltalk 代码的另一个分支允许在 SmallInteger
和任何其他类型的对象之间实现多态求和。例如,如果您评估 3 + 4.0
,Smalltalk 代码的这一部分将发生,因为在这种情况下,参数是 Float
。如果您评估 3 + (4 / 3)
等
,则会发生类似的情况
我知道一切皆对象,在 Smalltalk 中,您可以向对象发送消息来完成几乎所有事情。
现在我们如何实现一个对象(内存表示和基本操作)来表示原始数据类型?例如,整数的 +
是如何实现的?
我查看了 Smalltalk 的源代码并在 Smallint.st
中找到了这个。有人可以解释这段代码吗?
+ arg [
"Sum the receiver and arg and answer another Number"
<category: 'built ins'>
<primitive: VMpr_SmallInteger_plus>
^self generality == arg generality
ifFalse: [self retrySumCoercing: arg]
ifTrue: [(LargeInteger fromInteger: self) + (LargeInteger fromInteger: arg)]
]
这里是上面代码的 link: https://github.com/gnu-smalltalk/smalltalk/blob/62dab58e5231909c7286f1e61e26c9f503b2b3df/kernel/SmallInt.st
从概念上讲,原始 方法是由虚拟机 (VM) 而非常规 Smalltalk 代码实现的行为片段(例程)。
当 Smalltalk 编译器找到语句 <primitive: ...>
时,它会将此解释为一种特殊类型的方法,其参数(在您的例子中为 VMpr_SmallInteger_plus
)表示目标例程在 VM 中的整数索引。
从这个意义上说,原语是一个全局例程,不绑定到任何特定 class 的 MethodDictionary
。原始逻辑适用于某些 classes 的接收者和参数,这就是为什么它必须检查接收者和参数(如果有)是否符合其要求。如果不是,原语 失败 并且在这种情况下,控制流向 <primitive: ...>
语句之后的 Smalltalk 代码。否则原语 成功 并且下面的 Smalltalk 代码不会被执行。另请注意,编译器不允许在 <primitive:...>
句子上方出现临时声明以外的任何 Smalltalk 代码。
在您的示例中,如果参数 arg
不是预期的 class(大概是 SmallInteger
),例程将放弃尝试将其求和给接收者并委托将操作解析为 Smalltalk 代码。
如果参数恰好是 SmallInteger
,原语将计算结果(使用 VM 中保存的例程)并用它回答。
我还没有看到这个原语的代码,但是如果总和的结果不适合SmallInteger
,原语也可能会失败,在这种情况下,接收者和参数都会失败将转换为 LargeInteger
s 并且添加将在适当的 class(LargePositiveInteger
或 LargeNegativeInteger
)的 #+
方法中进行。
Smalltalk 代码的另一个分支允许在 SmallInteger
和任何其他类型的对象之间实现多态求和。例如,如果您评估 3 + 4.0
,Smalltalk 代码的这一部分将发生,因为在这种情况下,参数是 Float
。如果您评估 3 + (4 / 3)
等