教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 Golang反射机制的实现分析——reflect.Type类型名称

Golang反射机制的实现分析——reflect.Type类型名称

发布时间:2022-02-27   编辑:jiaochengji.com
教程集为您提供Golang反射机制的实现分析——reflect.Type类型名称等资源,欢迎您收藏本站,我们将为您提供最新的Golang反射机制的实现分析——reflect.Type类型名称资源
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/breaksoftware/article/details/85995767
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-5edb848729.css"/>

        现在越来越多的java、php或者python程序员转向了Golang。其中一个比较重要的原因是,它和C/C 一样,可以编译成机器码运行,这保证了执行的效率。在上述解释型语言中,它们都支持了“反射”机制,让程序员可以很方便的构建一些动态逻辑。这是C/C 相对薄弱的环节,而Golang却有良好的支持。本系列,我们将通过反汇编Golang的编译结果,探究其反射实现的机制。(转载请指明出于breaksoftware的csdn博客)

        为了防止编译器做优化,例子中的源码都通过下面的指令编译

<pre class="has"><code class="language-bash">go build -gcflags "-N -l" [xxxxxx].go</code></pre> <h1>类型名称</h1> <h2>基本类型</h2> <pre class="has"><code class="language-Go">package main import ( "fmt" "reflect" ) func main() { t := reflect.TypeOf(1) s := t.Name() fmt.Println(s) }</code></pre>

        这段代码最终将打印出1的类型——int。

        main函数的入口地址是main.main。我们使用gdb在这个位置下断点,然后反汇编。略去一部分函数准备工作,我们看到

<pre class="has"><code> 0x0000000000487c6f < 31>: mov %rbp,0xa0(%rsp) 0x0000000000487c77 < 39>: lea 0xa0(%rsp),%rbp 0x0000000000487c7f < 47>: lea 0xfb5a(%rip),%rax # 0x4977e0 0x0000000000487c86 < 54>: mov %rax,(%rsp) 0x0000000000487c8a < 58>: lea 0x40097(%rip),%rax # 0x4c7d28 <main.statictmp_0> 0x0000000000487c91 < 65>: mov %rax,0x8(%rsp) 0x0000000000487c96 < 70>: callq 0x46f210 <reflect.TypeOf></code></pre>

        第3~4行,这段代码将地址0x4977e0压栈。之后在5~6行,又将0x4c7d28压栈。64位系统下,程序的压栈不像32位系统使用push指令,而是使用mov指令间接操作rsp寄存器指向的栈空间。

        第7行,调用了reflect.TypeOf方法,在Golang的源码中,该方法的相关定义位于\src\reflect\type.go中

<pre class="has"><code class="language-Go">// TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) } // toType converts from a *rtype to a Type that can be returned // to the client of package reflect. In gc, the only concern is that // a nil *rtype must be replaced by a nil Type, but in gccgo this // function takes care of ensuring that multiple *rtype for the same // type are coalesced into a single Type. func toType(t *rtype) Type { if t == nil { return nil } return t }</code></pre>

        reflect.emptyInterface是一个保存数据类型信息和裸指针的结构体,它位于\src\reflect\value.go

<pre class="has"><code class="language-Go">// emptyInterface is the header for an interface{} value. type emptyInterface struct { typ *rtype word unsafe.Pointer }</code></pre>

        之前压栈的两个地址0x4977e0和0x4c7d28分别对应于type和word。

<pre class="has"><code>(gdb) x/16xb $rsp 0xc42003fed0: 0xe0 0x77 0x49 0x00 0x00 0x00 0x00 0x00 0xc42003fed8: 0x28 0x7d 0x4c 0x00 0x00 0x00 0x00 0x00</code></pre>

        这样在内存上便构成了一个emptyInterface结构。下面我们查看它们的内存,0x4c7d28保存的值0x01即是我们传入reflect.TypeOf的值。

<pre class="has"><code>0x4977e0: 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x4c7d28 <main.statictmp_0>: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00</code></pre>

        reflect.rtype定义位于src\reflect\type.go

<pre class="has"><code class="language-Go">// rtype is the common implementation of most values. // It is embedded in other struct types. // // rtype must be kept in sync with ../runtime/type.go:/^type._type. type rtype struct { size uintptr …… str nameOff // string form ptrToThis typeOff // type for pointer to this type, may be zero }</code></pre>

        在reflect.TypeOf方法中,我们看到reflect.toType隐式的将reflect.rtype转换成了reflect.Type类型,而reflect.Type类型和它完全不一样

<pre class="has"><code class="language-Go">type Type interface { Align() int FieldAlign() int Method(int) Method …… }</code></pre>

        从Golang的源码的角度去解析似乎进入了死胡同,我们继续转向汇编层面,查看reflect.TypeOf的实现

<pre class="has"><code> 0x000000000046f210 < 0>: mov 0x8(%rsp),%rax 0x000000000046f215 < 5>: test %rax,%rax 0x000000000046f218 < 8>: je 0x46f22c <reflect.TypeOf 28> 0x000000000046f21a < 10>: lea 0xaddbf(%rip),%rcx # 0x51cfe0 <go.itab.*reflect.rtype,reflect.Type> 0x000000000046f221 < 17>: mov %rcx,0x18(%rsp) 0x000000000046f226 < 22>: mov %rax,0x20(%rsp) 0x000000000046f22b < 27>: retq 0x000000000046f22c < 28>: xor

您可能感兴趣的文章:
更改MySQL数据库名实例代码
Golang反射机制的实现分析——reflect.Type类型名称
Golang反射机制的实现分析——reflect.Type方法查找和调用
PHP无限级分类菜单实例程序
Java反射(泛型擦除演示)的例子
Go语言--反射(reflect)
php实现简单用户登录功能程序代码
php session 同ip不同端口的多个网站session冲突的解决办法
js时间函数综合例子(日期计算、字符串转日期等)
mysql导入导出数据时中文乱码的解决办法

[关闭]
~ ~