0x00 相关概念

 在go语言里面interface用于定义一组行为,一个具体的类型只要实现了这一组行为,那么就可以称这个具体的类型实现了这个接口。下面是interface结构体

1
2
3
4
5
type interfacetype struct {
typ _type // 通用底层数据结构,任何一个类型的底层实现,都基于_type这一数据结构来实现
pkgpath name // 这是一个指向typename结构的地址
mhdr []imethod // 这个里面就是interface所定义的方法
}

imethod结构体定义如下:

1
2
3
4
type imethod struct {
name nameOff // int32类型,相对于firstmoduledata.type的偏移量,计算后的地址指向一个type name结构体
ityp typeOff // int32类型,相对于firstmoduledata.type的偏移量,方法声明的信息,计算后执行一个func type结构体
}

 在go语言里面interface和其他类型之间的映射关系是通过interface映射表来实现的(itab)(有点类似C++里面的vtab), itab的结构体如下:

1
2
3
4
5
6
7
8
9
type itab struct {
inter *interfacetype // 指向一个interface类型的定义
_type *_type // 指向一个普通数据类型,另外这个普通类型_type 还需要实现上inter接口所有的方法
hash uint32 // copy of _type.hash. Used for type switches.
_ [4]byte
// fun 指向一组方法, 指向的上具体类型_type(上面第二个字段)的方法
// 这个指针数组是可以变化的,编译器负责填充,在下面的例子我们将会看到这一点
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

0x01 接口调用过程分析

 一段简单的demo代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

type I interface{
DoSomeWork()
}

type T struct{
a int
}

func(t *T)DoSomeWork(){

}


func main(){
t := &T{}
i := I(t)
print(i)
}

生成AST语法树如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
before (*T).DoSomeWork
after walk (*T).DoSomeWork

before main
. DCL l(17)
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T

. AS l(17) colas(1) tc(1)
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T
. . PTRLIT l(17) esc(no) ld(1) tc(1) PTR64-*main.T
. . . STRUCTLIT l(17) tc(1) main.T
. . . . TYPE <S> l(17) tc(1) implicit(1) type=PTR64-*main.T PTR64-*main.T

. DCL l(18)
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I

. AS l(18) tc(1)
. . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T

. AS l(18) colas(1) tc(1)
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I
. . CONVIFACE l(18) tc(1) main.I
. . . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T

. VARKILL l(18) tc(1)
. . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T

. PRINT l(19) tc(1)
. PRINT-list
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I
after walk main
. DCL l(17)
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T

. AS-init
. . AS l(17) tc(1)
. . . NAME-main.autotmp_0002 u(1) a(1) l(17) x(0+0) class(PAUTO) esc(N) tc(1) used(1) main.T

. . AS l(17) tc(1)
. . . NAME-main.autotmp_0001 u(1) a(1) l(17) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T
. . . ADDR u(2) l(17) tc(1) PTR64-*main.T
. . . . NAME-main.autotmp_0002 u(1) a(1) l(17) x(0+0) class(PAUTO) esc(N) tc(1) used(1) main.T

. . AS u(2) l(17) tc(1)
. . . IND u(2) l(17) tc(1) main.T
. . . . NAME-main.autotmp_0001 u(1) a(1) l(17) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T
. AS u(100) l(17) tc(1)
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T
. . NAME-main.autotmp_0001 u(1) a(1) l(17) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T

. DCL l(18)
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I

. AS u(2) l(18) tc(1)
. . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T

. AS-init
. . AS l(18) tc(1)
. . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . NAME-go.itab.*"".T."".I u(1) a(1) l(18) x(0+0) class(PEXTERN) tc(1) used(1) PTR64-*uint8

. . IF l(18) tc(1)
. . IF-test
. . . EQ l(18) tc(1) bool
. . . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . . LITERAL-nil u(1) a(1) l(18) tc(1) PTR64-*uint8
. . IF-body
. . . AS l(18) tc(1)
. . . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . . CALLFUNC u(100) l(18) tc(1) PTR64-*byte
. . . . . NAME-runtime.typ2Itab u(1) a(1) l(2) x(0+0) class(PFUNC) tc(1) used(1) FUNC-funcSTRUCT-(FIELD-
. . . . . . NAME-runtime.typ·2 u(1) a(1) l(2) x(0+0) class(PPARAM) f(1) PTR64-*byte PTR64-*byte, FIELD-
. . . . . . NAME-runtime.typ2·3 u(1) a(1) l(2) x(8+0) class(PPARAM) f(1) PTR64-*byte PTR64-*byte, FIELD-
. . . . . . NAME-runtime.cache·4 u(1) a(1) l(2) x(16+0) class(PPARAM) f(1) PTR64-*PTR64-*byte PTR64-*PTR64-*byte) PTR64-*byte
. . . . CALLFUNC-list
. . . . . AS u(2) l(18) tc(1)
. . . . . . INDREG-SP a(1) l(18) x(0+0) tc(1) runtime.typ·2 G0 PTR64-*byte
. . . . . . ADDR u(2) a(1) l(18) tc(1) PTR64-*uint8
. . . . . . . NAME-type.*"".T u(1) a(1) l(11) x(0+0) class(PEXTERN) tc(1) uint8

. . . . . AS u(2) l(18) tc(1)
. . . . . . INDREG-SP a(1) l(18) x(8+0) tc(1) runtime.typ2·3 G0 PTR64-*byte
. . . . . . ADDR u(2) a(1) l(18) tc(1) PTR64-*uint8
. . . . . . . NAME-type."".I u(1) a(1) l(18) x(0+0) class(PEXTERN) tc(1) uint8

. . . . . AS u(2) l(18) tc(1)
. . . . . . INDREG-SP a(1) l(18) x(16+0) tc(1) runtime.cache·4 G0 PTR64-*PTR64-*byte
. . . . . . ADDR u(2) a(1) l(18) tc(1) PTR64-*PTR64-*uint8
. . . . . . . NAME-go.itab.*"".T."".I u(1) a(1) l(18) x(0+0) class(PEXTERN) tc(1) used(1) PTR64-*uint8
. AS u(100) l(18) tc(1)
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I
. . EFACE u(2) l(18) tc(1) main.I
. . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T

. VARKILL l(18) tc(1)
. . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T

. EMPTY-init
. . CALLFUNC u(100) l(19) tc(1)
. . . NAME-runtime.printiface u(1) a(1) l(2) x(0+0) class(PFUNC) tc(1) used(1) FUNC-funcSTRUCT-(FIELD-main.I)
. . CALLFUNC-list
. . . AS u(1) l(19) tc(1)
. . . . INDREG-SP a(1) l(19) x(0+0) tc(1) main.I
. . . . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I
. EMPTY u(100) l(19) tc(1)

before init
. IF l(20) tc(1)
. IF-test
. . NE l(20) tc(1) bool
. . . NAME-main.initdone· u(1) a(1) l(20) x(0+0) class(PEXTERN) tc(1) used(1) uint8
. . . LITERAL-0 u(1) a(1) l(20) tc(1) uint8
. IF-body
. . IF l(20) tc(1)
. . IF-test
. . . EQ l(20) tc(1) bool
. . . . NAME-main.initdone· u(1) a(1) l(20) x(0+0) class(PEXTERN) tc(1) used(1) uint8
. . . . LITERAL-2 u(1) a(1) l(20) tc(1) uint8
. . IF-body
. . . RETURN l(20) tc(1)

. . CALLFUNC l(20) tc(1)
. . . NAME-runtime.throwinit u(1) a(1) l(2) x(0+0) class(PFUNC) tc(1) used(1) FUNC-funcSTRUCT-()

. AS l(20) tc(1)
. . NAME-main.initdone· u(1) a(1) l(20) x(0+0) class(PEXTERN) tc(1) used(1) uint8
. . LITERAL-1 u(1) a(1) l(20) tc(1) uint8

. AS l(20) tc(1)
. . NAME-main.initdone· u(1) a(1) l(20) x(0+0) class(PEXTERN) tc(1) used(1) uint8
. . LITERAL-2 u(1) a(1) l(20) tc(1) uint8

. RETURN l(20) tc(1)
after walk init
. IF l(20) tc(1)
. IF-test
. . NE u(2) l(20) tc(1) bool
. . . NAME-main.initdone· u(1) a(1) l(20) x(0+0) class(PEXTERN) tc(1) used(1) uint8
. . . LITERAL-0 u(1) a(1) l(20) tc(1) uint8
. IF-body
. . IF l(20) tc(1)
. . IF-test
. . . EQ u(2) l(20) tc(1) bool
. . . . NAME-main.initdone· u(1) a(1) l(20) x(0+0) class(PEXTERN) tc(1) used(1) uint8
. . . . LITERAL-2 u(1) a(1) l(20) tc(1) uint8
. . IF-body
. . . RETURN l(20) tc(1)

. . CALLFUNC u(100) l(20) tc(1)
. . . NAME-runtime.throwinit u(1) a(1) l(2) x(0+0) class(PFUNC) tc(1) used(1) FUNC-funcSTRUCT-()

. AS u(2) l(20) tc(1)
. . NAME-main.initdone· u(1) a(1) l(20) x(0+0) class(PEXTERN) tc(1) used(1) uint8
. . LITERAL-1 u(1) a(1) l(20) tc(1) uint8

. AS u(2) l(20) tc(1)
. . NAME-main.initdone· u(1) a(1) l(20) x(0+0) class(PEXTERN) tc(1) used(1) uint8
. . LITERAL-2 u(1) a(1) l(20) tc(1) uint8

. RETURN l(20) tc(1)

before I.DoSomeWork
. CALLINTER l(22) tc(1)
. . DOTINTER l(22) x(0+0) tc(1) FUNC-methodSTRUCT-(FIELD-PTR64-*STRUCT-struct {}) funcSTRUCT-()
. . . NAME-main..this u(1) a(1) g(1) l(22) x(0+0) class(PPARAM) f(1) tc(1) used(1) main.I
. . . NAME-main.DoSomeWork u(1) a(1) l(22) x(0+0)
after walk I.DoSomeWork
. CALLINTER u(100) l(22) tc(1)
. . DOTINTER u(2) l(22) x(0+0) tc(1) FUNC-methodSTRUCT-(FIELD-PTR64-*STRUCT-struct {}) funcSTRUCT-()
. . . NAME-main..this u(1) a(1) g(1) l(22) x(0+0) class(PPARAM) f(1) tc(1) used(1) main.I
. . . NAME-main.DoSomeWork u(1) a(1) l(22) x(0+0)

before main节点树
先看分析下第一节点DCL第二个节点AS两个节点

1
2
3
4
5
6
7
8
DCL l(17)
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T

. AS l(17) colas(1) tc(1)
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T
. . PTRLIT l(17) esc(no) ld(1) tc(1) PTR64-*main.T
. . . STRUCTLIT l(17) tc(1) main.T
. . . . TYPE <S> l(17) tc(1) implicit(1) type=PTR64-*main.T PTR64-*main.T

第一个节点DCL 声明了一个变量main.t,变量类型是一个64位指向main.T的指针,
第二个节点AS 节点将一个main.T实力的指针赋值给main.T
这两段的伪代码如下:

1
2
var main.t *main.T
main.t = &main.T{}

接着看下第三节点DCL和第四第五个AS节点:

1
2
3
4
5
6
7
8
9
10
11
12

. DCL l(18)
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I

. AS l(18) tc(1)
. . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T
. . NAME-main.t u(1) a(1) g(1) l(17) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) PTR64-*main.T

. AS l(18) colas(1) tc(1)
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I
. . CONVIFACE l(18) tc(1) main.I
. . . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T

第三个节点DCL声明了一个变量main.i, 类型是main.I
第四个节点AS节点创建一个临时对象main.autotmp_000,类型是*main.T, 并把main.t 赋值给main.autotmp_000
第五个节点AS节点,创建一个特殊的节点CONVIFACE,并且把这个节点赋值给main.i
这三个节点伪代码如下:

1
2
3
var main.i main.I
var main.autotmp_000 = main.t
main.i = CONVIFACE(main.autotmp_000)

看下编译器处理后的第二个版本的节点树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 AS-init
. . AS l(18) tc(1)
. . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . NAME-go.itab.*"".T."".I u(1) a(1) l(18) x(0+0) class(PEXTERN) tc(1) used(1) PTR64-*uint8

. . IF l(18) tc(1)
. . IF-test
. . . EQ l(18) tc(1) bool
. . . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . . LITERAL-nil u(1) a(1) l(18) tc(1) PTR64-*uint8
. . IF-body
. . . AS l(18) tc(1)
. . . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . . CALLFUNC u(100) l(18) tc(1) PTR64-*byte
. . . . . NAME-runtime.typ2Itab u(1) a(1) l(2) x(0+0) class(PFUNC) tc(1) used(1) FUNC-funcSTRUCT-(FIELD-
. . . . . . NAME-runtime.typ·2 u(1) a(1) l(2) x(0+0) class(PPARAM) f(1) PTR64-*byte PTR64-*byte, FIELD-
. . . . . . NAME-runtime.typ2·3 u(1) a(1) l(2) x(8+0) class(PPARAM) f(1) PTR64-*byte PTR64-*byte, FIELD-
. . . . . . NAME-runtime.cache·4 u(1) a(1) l(2) x(16+0) class(PPARAM) f(1) PTR64-*PTR64-*byte PTR64-*PTR64-*byte) PTR64-*byte
. . . . CALLFUNC-list
. . . . . AS u(2) l(18) tc(1)
. . . . . . INDREG-SP a(1) l(18) x(0+0) tc(1) runtime.typ·2 G0 PTR64-*byte
. . . . . . ADDR u(2) a(1) l(18) tc(1) PTR64-*uint8
. . . . . . . NAME-type.*"".T u(1) a(1) l(11) x(0+0) class(PEXTERN) tc(1) uint8

. . . . . AS u(2) l(18) tc(1)
. . . . . . INDREG-SP a(1) l(18) x(8+0) tc(1) runtime.typ2·3 G0 PTR64-*byte
. . . . . . ADDR u(2) a(1) l(18) tc(1) PTR64-*uint8
. . . . . . . NAME-type."".I u(1) a(1) l(18) x(0+0) class(PEXTERN) tc(1) uint8

. . . . . AS u(2) l(18) tc(1)
. . . . . . INDREG-SP a(1) l(18) x(16+0) tc(1) runtime.cache·4 G0 PTR64-*PTR64-*byte
. . . . . . ADDR u(2) a(1) l(18) tc(1) PTR64-*PTR64-*uint8
. . . . . . . NAME-go.itab.*"".T."".I u(1) a(1) l(18) x(0+0) class(PEXTERN) tc(1) used(1) PTR64-*uint8
. AS u(100) l(18) tc(1)
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I
. . EFACE u(2) l(18) tc(1) main.I
. . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T

AS-init节点里面主要干了以下事情:临时创建一个对象main.autotmp_0003,并且把go.itab.*"".T."".I赋值给main.autotmp_0003, 然后判断这个对象是否为空,如果为空则调用runtime.typ2Itab方法

1
2
3
4
5
func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab {
tab := getitab(inter, t, false)
atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
return tab
}

真正工作的函数是getitab(inter, t, false)方法
getitab函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
if len(inter.mhdr) == 0 {
gothrow("internal error - misuse of itab")
}

// easy case
x := typ.x
if x == nil {
if canfail {
return nil
}
i := (*imethod)(add(unsafe.Pointer(inter), unsafe.Sizeof(interfacetype{})))
panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *i.name})
}

// compiler has provided some good hash codes for us.
h := inter.typ.hash
h += 17 * typ.hash
// TODO(rsc): h += 23 * x.mhash ?
h %= hashSize

// look twice - once without lock, once with.
// common case will be no lock contention.
var m *itab
var locked int
for locked = 0; locked < 2; locked++ {
if locked != 0 {
lock(&ifaceLock)
}
for m = (*itab)(atomicloadp(unsafe.Pointer(&hash[h]))); m != nil; m = m.link {
if m.inter == inter && m._type == typ {
if m.bad != 0 {
m = nil
if !canfail {
// this can only happen if the conversion
// was already done once using the , ok form
// and we have a cached negative result.
// the cached result doesn't record which
// interface function was missing, so jump
// down to the interface check, which will
// do more work but give a better error.
goto search
}
}
if locked != 0 {
unlock(&ifaceLock)
}
return m
}
}
}
// 上面我们提过itab.fun这个指针数组的大小是动态
m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr))*ptrSize, 0, &memstats.other_sys))
m.inter = inter
m._type = typ

search:
// both inter and typ have method sorted by name,
// and interface names are unique,
// so can iterate over both in lock step;
// the loop is O(ni+nt) not O(ni*nt).
ni := len(inter.mhdr)
nt := len(x.mhdr)
j := 0
for k := 0; k < ni; k++ {
// 这里面有两层循环,目的是遍历interface定义的所有的方法
// 在遍历具体类型所定义的方法
// 如果匹配到,则把它放到interface的mhdr里面。
i := (*imethod)(add(unsafe.Pointer(inter), unsafe.Sizeof(interfacetype{})+uintptr(k)*unsafe.Sizeof(imethod{})))
iname := i.name
ipkgpath := i.pkgpath
itype := i._type
for ; j < nt; j++ {
t := (*method)(add(unsafe.Pointer(x), unsafe.Sizeof(uncommontype{})+uintptr(j)*unsafe.Sizeof(method{})))
if t.mtyp == itype && t.name == iname && t.pkgpath == ipkgpath {
if m != nil {
*(*unsafe.Pointer)(add(unsafe.Pointer(m), unsafe.Sizeof(itab{})+uintptr(k)*ptrSize)) = t.ifn
}
goto nextimethod
}
}
// didn't find method
if !canfail {
if locked != 0 {
unlock(&ifaceLock)
}
panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *iname})
}
m.bad = 1
break
nextimethod:
}
if locked == 0 {
gothrow("invalid itab locking")
}
m.link = hash[h]
atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
unlock(&ifaceLock)
if m.bad != 0 {
return nil
}
return m
}

再看下最后的AS节点:

1
2
3
4
5
.   AS u(100) l(18) tc(1)
. . NAME-main.i u(1) a(1) g(2) l(18) x(0+0) class(PAUTO) f(1) ld(1) tc(1) used(1) main.I
. . EFACE u(2) l(18) tc(1) main.I
. . . NAME-main.autotmp_0003 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*uint8
. . . NAME-main.autotmp_0000 u(1) a(1) l(18) x(0+0) class(PAUTO) esc(N) tc(1) used(1) PTR64-*main.T

这里将EFACE节点赋给main.i变量,EFACE里面包含了一个指向autotmp_0003变量(autotmp_0003就是上面生成itab的那个变量) 和一个指针变量autotmp_0000(这个其实就是*main.T)
因此main.i里面其实就包含了两部分

1
2
3
4
type iface struct {
tab *itab
data unsafe.Pointer
}

0x02 总结

 当通过接口调用方法的时候大体过程如下:

1
2
3
4
5
6
7
8
var t *T
var i *I
var tmp *T
tmp = t
i = interface{
tab: getitab()
data: tmp
}

从上面伪代码可以看出

  1. interface自己本身就是一个变量
  2. interface有两部分,第一部分是一个itab映射表,一个是指向具体对象的指针

0x03 参考

  1. 《go语言核心编程》
  2. golang AST简介
  3. drive into compiler