0x00 相关概念 在go语言里面interface用于定义一组行为,一个具体的类型只要实现了这一组行为,那么就可以称这个具体的类型实现了这个接口。下面是interface结构体
1 2 3 4 5 type interfacetype struct { typ _type pkgpath name mhdr []imethod }
imethod结构体定义如下:
1 2 3 4 type imethod struct { name nameOff ityp typeOff }
在go语言里面interface和其他类型之间的映射关系是通过interface映射表来实现的(itab)(有点类似C++里面的vtab), itab的结构体如下:
1 2 3 4 5 6 7 8 9 type itab struct { inter *interfacetype _type *_type hash uint32 _ [4 ]byte fun [1 ]uintptr }
0x01 接口调用过程分析 一段简单的demo代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package maintype 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.Tmain.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.Ivar main.autotmp_000 = main.tmain.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" ) } 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}) } h := inter.typ.hash h += 17 * typ.hash h %= hashSize 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 { goto search } } if locked != 0 { unlock(&ifaceLock) } return m } } } m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr (len (inter.mhdr))*ptrSize, 0 , &memstats.other_sys)) m.inter = inter m._type = typ search: ni := len (inter.mhdr) nt := len (x.mhdr) j := 0 for k := 0 ; k < ni; k++ { 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 } } 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 *Tvar i *Ivar tmp *Ttmp = t i = interface { tab: getitab() data: tmp }
从上面伪代码可以看出
interface自己本身就是一个变量
interface有两部分,第一部分是一个itab映射表,一个是指向具体对象的指针
0x03 参考
《go语言核心编程》