itab 结构实际上如何具有函数指针列表?

How is the itab struct actually having a list of function pointers?

研究 go 中的接口值 - 我发现了 Russ Cox 的一个很棒的(可能已经过时了)article。 根据它:

The itable begins with some metadata about the types involved and then becomes a list of function pointers.

这个 itable 的实现 应该是 来自 src/runtime/runtime2 的实现:

type itab struct {
    inter *interfacetype
    _type *_type
    hash  uint32 // copy of _type.hash. Used for type switches.
    _     [4]byte
    fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

首先令人困惑的是 - 数组的大小如何变化? 其次,假设我们在索引 0 处有一个满足接口的方法的函数指针,我们可以在哪里存储 second/third/... 函数指针?

编译后的代码和运行时访问 fun 就好像声明了字段 fun [n]uintpr 其中 n 是接口中方法的数量。第二种方法存储在 fun[1],第三种存储在 fun[2],依此类推。 Go 语言没有像这样的可变大小数组特性,但是 unsafe 恶作剧可以用来模拟这个特性。

方法如下 itab is allocated:

m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*goarch.PtrSize, 0, &memstats.other_sys))

函数persistentalloc分配内存。该函数的第一个参数是要分配的大小。表达式inter.mhdr是接口中的方法数。

这里 code 在可变大小数组上创建一个切片:

methods := (*[1 << 16]unsafe.Pointer)(unsafe.Pointer(&m.fun[0]))[:ni:ni]

表达式 methods[i] 与假设世界中的 m.fun[i] 指代相同的元素,其中 m.fun 是长度 > i 的可变大小数组。后面的代码使用带 methods 的普通切片语法来访问可变大小数组 m.fun.