go源码阅读——value.go

Posted Wang-Junchao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go源码阅读——value.go相关的知识,希望对你有一定的参考价值。

【博文目录>>>】 【项目地址>>>】


基本内容

value文件主要提供值的一些调用方法,value所需要的值在在type.go文件中定义

package reflect

import (
	"math"
	"runtime"
	"unsafe"
)

const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const

// Value is the reflection interface to a Go value.
//
// Not all methods apply to all kinds of values. Restrictions,
// if any, are noted in the documentation for each method.
// Use the Kind method to find out the kind of value before
// calling kind-specific methods. Calling a method
// inappropriate to the kind of type causes a run time panic.
//
// The zero Value represents no value.
// Its IsValid method returns false, its Kind method returns Invalid,
// its String method returns "<invalid Value>", and all other methods panic.
// Most functions and methods never return an invalid value.
// If one does, its documentation states the conditions explicitly.
//
// A Value can be used concurrently by multiple goroutines provided that
// the underlying Go value can be used concurrently for the equivalent
// direct operations.
//
// To compare two Values, compare the results of the Interface method.
// Using == on two Values does not compare the underlying values
// they represent.
/**
 *  Value是Go值的反射接口。
 *
 * 并非所有方法都适用于所有类型的值。 在每种方法的文档中都注明了限制(如果有)。
 * 在调用特定于种类的方法之前,使用Kind方法找出值的种类。 调用不适合该类型的方法会导致运行时恐慌。
 *
 * 零值表示无值。
 * 它的IsValid方法返回false,其Kind方法返回Invalid,其String方法返回"<invalid Value>",所有其他方法均会出现恐慌情况。
 * 大多数函数和方法从不返回无效值。
 * 如果是,则其文档会明确说明条件。
 *
 * 一个值可以由多个goroutine并发使用,前提是可以将基础Go值同时用于等效的直接操作。
 *
 * 要比较两个值,请比较Interface方法的结果。
 * 在两个值上使用==不会比较它们表示的基础值。
 */
type Value struct 
	// typ holds the type of the value represented by a Value.
	/**
	 * typ包含由值表示的值的类型。
	 */
	typ *rtype

	// Pointer-valued data or, if flagIndir is set, pointer to data.
	// Valid when either flagIndir is set or typ.pointers() is true.
	/**
	 * 指针值的数据;如果设置了flagIndir,则为数据的指针。
     * 在设置flagIndir或typ.pointers()为true时有效。
	 */
	ptr unsafe.Pointer

	// flag holds metadata about the value.
	// The lowest bits are flag bits:
	//	- flagStickyRO: obtained via unexported not embedded field, so read-only
	//	- flagEmbedRO: obtained via unexported embedded field, so read-only
	//	- flagIndir: val holds a pointer to the data
	//	- flagAddr: v.CanAddr is true (implies flagIndir)
	//	- flagMethod: v is a method value.
	// The next five bits give the Kind of the value.
	// This repeats typ.Kind() except for method values.
	// The remaining 23+ bits give a method number for method values.
	// If flag.kind() != Func, code can assume that flagMethod is unset.
	// If ifaceIndir(typ), code can assume that flagIndir is set.
	/**
	 * 标志保存有关该值的元数据。
     * 最低位是标志位:
     * -flagStickyRO:通过未导出的未嵌入字段获取,因此为只读
     * -flagEmbedRO:通过未导出的嵌入式字段获取,因此为只读
     * -flagIndir:val保存指向数据的指针
     * -flagAddr:v.CanAddr为true(暗示flagIndir)
     * -flagMethod:v是方法值。
     * 接下来的五位给出值的种类。
     * 重复typ.Kind()中的值,方法值除外。
     * 其余的23+位给出方法值的方法编号。
     * 如果flag.kind() != Func,则代码可以假定flagMethod未设置。
     * 如果是ifaceIndir(typ),则代码可以假定设置了flagIndir。
	 */
	flag

	// A method value represents a curried method invocation
	// like r.Read for some receiver r. The typ+val+flag bits describe
	// the receiver r, but the flag's Kind bits say Func (methods are
	// functions), and the top bits of the flag give the method number
	// in r's type's method table.
	/**
	 * 方法值表示类似r.Read的经过当前(curried)的方法调用,用于某些接收方r。
     * typ + val + flag位描述接收方r,但标志的Kind位表示Func(方法是函数),
     * 并且标志的高位给出r的类型的方法表中的方法编号。
	 */


type flag uintptr

/**
 * flag类型是uintptr,此类型文档并未说明是多少位,只是说位数足够多,可以纳任何指针的位模式
 * 为了方便解理,这里假设uintptr是uint32
 * flag的位模式分成几组,从高位到底位:[31:10][9][8][7][6][5][4:0]
 * [4:0]: 第0~4位:共计5位,用于表示值类型,最多可表示32种类型,值类型参见同包下的,type.go Kind枚举
 * [5]: 第5位:只读类型,设置为1表示:通过未导出的未嵌入字段获取
 * [6]: 第6位:只读类型,设置为1表示:通过未导出的嵌入式字段获取
 * [7]: 第7位:间接指针标记,设置为1表示:Value的val属性保存指向数据的指针
 * [8]: 第8位:可取址标记,设置为1表示:可取址,并且暗示flagIndir已经设置成1
 * [9]: 第9位:方法标记,对于Value类型,第9位为1表示其是方法类型,
 * [31:10]: 第10~31位:只对于Value是方法类型有用,用于表示第i个方法,将i<<flagMethodShift(10)位得到
 *
 */
const (
	flagKindWidth        = 5 // there are 27 kinds // 种类的宽度,具体种类在type.go文件中
	flagKindMask    flag = 1<<flagKindWidth - 1 // 数据类型的掩码
	flagStickyRO    flag = 1 << 5 // 通过未导出的未嵌入字段获取,因此为只读
	flagEmbedRO     flag = 1 << 6 // 通过未导出的嵌入式字段获取,因此为只读
	flagIndir       flag = 1 << 7 // val保存指向数据的指针,val指的是Value中的ptr属性
	flagAddr        flag = 1 << 8 // v.CanAddr为true(暗示flagIndir),v指Value的实例
	flagMethod      flag = 1 << 9 // v是方法值,v指Value的实例
	flagMethodShift      = 10 // 计算是第几个方法需要的位移数
	flagRO          flag = flagStickyRO | flagEmbedRO // 表示是否只读
)

/**
 * 求数值类型
 * @return 数值类型
 **/
func (f flag) kind() Kind 
	return Kind(f & flagKindMask)


/**
 * 获取只读标记
 */
func (f flag) ro() flag 
	if f&flagRO != 0 
		return flagStickyRO
	
	return 0


// pointer returns the underlying pointer represented by v.
// v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer
/**
 * 指针返回v表示的底层指针。
 * v.Kind()必须是Ptr,Map,Chan,Func或UnsafePointer
 * @return
 **/
func (v Value) pointer() unsafe.Pointer 
	if v.typ.size != ptrSize || !v.typ.pointers() 
		panic("can't call pointer on a non-pointer Value")
	
	if v.flag&flagIndir != 0 
		return *(*unsafe.Pointer)(v.ptr)
	
	return v.ptr


// packEface converts v to the empty interface.
/**
 * packEface将v转换为空接口。
 * @return 空接口
 **/
func packEface(v Value) interface 
	t := v.typ
	var i interface
	e := (*emptyInterface)(unsafe.Pointer(&i))
	// First, fill in the data portion of the interface.
	// 首先,填写接口的数据部分。
	switch 
	case ifaceIndir(t): // ifaceIndir报告t是否间接存储在接口值中。
		if v.flag&flagIndir == 0 
			panic("bad indir")
		
		// Value is indirect, and so is the interface we're making.
		// 值是间接的,我们正在建立的接口也是间接的。
		ptr := v.ptr
		if v.flag&flagAddr != 0 
			// TODO: pass safe boolean from valueInterface so
			// we don't need to copy if safe==true?
			// TODO:从valueInterface传递安全布尔值,因此如果safe == true,我们不需要复制吗?
			c := unsafe_New(t) // 在runtime包中实验,用于创建一个Pointer类型
			typedmemmove(t, c, ptr) // typedmemmove将类型t的值从prt复制到c。
			ptr = c
		
		e.word = ptr
	case v.flag&flagIndir != 0:
		// Value is indirect, but interface is direct. We need
		// to load the data at v.ptr into the interface data word.
		// 值是间接的,但接口是直接的。 我们需要将v.ptr处的数据加载到接口数据字中。
		e.word = *(*unsafe.Pointer)(v.ptr)
	default:
		// Value is direct, and so is the interface.
		// 值是直接的,接口也是直接的。
		e.word = v.ptr
	
	// Now, fill in the type portion. We're very careful here not
	// to have any operation between the e.word and e.typ assignments
	// that would let the garbage collector observe the partially-built
	// interface value.
	// 现在,填写类型部分。 在这里,我们非常小心,不要在e.word和e.typ分配之间进行任何操作,
	// 以免垃圾回收器观察部分构建的接口值。
	e.typ = t
	return i


// unpackEface converts the empty interface i to a Value.
/**
 * unpackEface将空接口i转换为Value。
 * @param 接口
 * @return Value类型
 **/
func unpackEface(i interface) Value 
	e := (*emptyInterface)(unsafe.Pointer(&i))
	// NOTE: don't read e.word until we know whether it is really a pointer or not.
	// 注意:在我们知道e.word是否真的是指针之前,不要读它。
	t := e.typ
	if t == nil  // i对应的类型为空,则不需要设置相关值
		return Value
	
	f := flag(t.Kind())
	if ifaceIndir(t)  // 设置接口值标记
		f |= flagIndir
	
	return Valuet, e.word, f


// A ValueError occurs when a Value method is invoked on
// a Value that does not support it. Such cases are documented
// in the description of each method.
/**
 * 在不支持Value的Value方法上调用Value方法时,发生ValueError。
 * 在每种方法的说明中都记录了这种情况。
 */
type ValueError struct 
	Method string
	Kind   Kind


func (e *ValueError) Error() string 
	if e.Kind == 0 
		return "reflect: call of " + e.Method + " on zero Value"
	
	return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value"


// methodName returns the name of the calling method,
// assumed to be two stack frames above.
/**
 * methodName返回调用方法的名称,假定为上面有两个堆栈帧。
 */
func methodName() string 
    /**
     * func Caller(skip int) (pc uintptr, file string, line int, ok bool)
     * Caller报告有关调用goroutine堆栈上函数调用的文件和行号信息。
     * 参数skip是要提升的堆栈帧数,其中0标识Caller的调用者。
     *(由于历史原因,在Caller和Callers之间,跳过的含义有所不同。)
     * 返回值报告相应调用文件中的程序计数器,文件名和行号。 如果该信息不可能恢复,
     * 则ok布尔值为False。
     */
	pc, _, _, _ := runtime.Caller(2) // Question: 为什么是2?
	/**
	 * 给定程序计数器地址,否则为nil。
     * 如果pc由于内联而表示多个函数,它将返回一个* Func描述最内部的函数,
     * 但带有最外部的函数的条目。
	 */
	f := runtime.FuncForPC(pc)
	if f == nil 
		return "unknown method"
	
	return f.Name()


// emptyInterface is the header for an interface value.
/**
 * emptyInterface是interface值的头部。
 */
type emptyInterface struct 
	typ  *rtype
	word unsafe.Pointer


// nonEmptyInterface is the header for an interface value with methods.
/**
 * nonEmptyInterface是带有方法的接口值的头部。
 */
type nonEmptyInterface struct 
	// see ../runtime/iface.go:/Itab
	itab *struct 
		ityp *rtype // static interface type // 静态接口类型
		typ  *rtype // dynamic concrete type // 动态创建类型
		hash uint32 // copy of typ.hash      // hash值
		_    [4]byte                         // Question: 用于对齐?
		fun  [100000]unsafe.Pointer // method table*  Question: 最多保存10W个方法?
	
	word unsafe.Pointer


// mustBe panics if f's kind is not expected.
// Making this a method on flag instead of on Value
// (and embedding flag in Value) means that we can write
// the very clear v.mustBe(Bool) and have it compile into
// v.flag.mustBe(Bool), which will only bother to copy the
// single important word for the receiver.
/**
 * 如果f的种类不是期望类型,则必须惊慌。
 * 将此方法设置为基于标志而不是基于Value的方法(并在Value中嵌入flag标志)
 * 意味着我们可以编写非常清晰的v.mustBe(Bool)并将其编译为v.flag.mustBe(Bool),
 * 唯一麻烦是只需要为接收者复制一个重要的单词。
 */
func (f flag) mustBe(expected Kind) 
	// TODO(mvdan): use f.kind() again once mid-stack inlining gets better
	// TODO(mvdan): mid-stack的内联变得更好后,再次使用f.kind()
    // Question: mid-stack是什么?
	if Kind(f&flagKindMask) != expected 
		panic(&ValueErrormethodName(), f.kind())
	


// mustBeExported panics if f records that the value was obtained using
// an unexported field.
/**
 * 如果f记录了使用未导出字段获得的值,则必须惊慌。
 */
func (f flag) mustBeExported() 
    // Enhance: mustBeExported和mustBeExportedSlow两个方法一样,
	if f == 0 || f&flagRO != 0 
		f.mustBeExportedSlow()
	


func (f flag) mustBeExportedSlow() 
	if f == 0 
		panic(&ValueErrormethodName(), Invalid)
	
	if f&flagRO != 0 
		panic("reflect: " + methodName() + " using value obtained using unexported field")
	


// mustBeAssignable panics if f records that the value is not assignable,
// which is to say that either it was obtained using an unexported field
// or it is not addressable.
/**
 * 如果f记录该值不可分配,则mustBeAssignable会发生panic,
 * 这意味着它是使用未导出的字段获取的,或者它是不可寻址的。
 */
func (f flag) mustBeAssignable() 
	if f&flagRO != 0 || f&flagAddr == 0 
		f.mustBeAssignableSlow()
	


func (f flag) mustBeAssignableSlow() 
	if f == 0 
		panic(&ValueErrormethodName(), Invalid)
	
	// Assignable if addressable and not read-only.
	// 如果可寻址且不是只读,则可分配。
	if f&flagRO != 0 
		panic("reflect: " + methodName() + " using value obtained using unexported field")
	
	if f&flagAddr == 0 
		panic("reflect: " + methodName() + " using unaddressable value")
	


// Addr returns a pointer value representing the address of v.
// It panics if CanAddr() returns false.
// Addr is typically used to obtain a pointer to a struct field
// or slice element in order to call a method that requires a
// pointer receiver.
/**
 * Addr返回表示v地址的指针值。
 * 如果CanAddr()返回false,则会感到恐慌。
 * Addr通常用于获取指向struct字段或slice元素的指针,以便调用需要指针接收器的方法。
 */
func (v Value) Addr() Value 
	if v.flag&flagAddr == 0 
		panic("reflect.Value.Addr of unaddressable value")
	
	return Valuev.typ.ptrTo(), v.ptr, v.flag.ro() | flag(Ptr) // Ptr是指标类型的类型值


// Bool returns v's underlying value.
// It panics if v's kind is not Bool.
/**
 * Bool返回v的基础值,如果v的种类不是Bool则会恐慌。
 */
func (v Value) Bool() bool 
	v.mustBe(Bool)
	return *(*bool)(v.ptr) // 先转成bool类型指针,再取值


// Bytes returns v's underlying value.
// It panics if v's underlying value is not a slice of bytes.
/**
 * 字节返回v的底层值,如果v的底层值不是字节的片段则恐慌。
 */
func (v Value) Bytes() []byte 
	v.mustBe(Slice)
	if v.typ.Elem().Kind() != Uint8 
		panic("reflect.Value.Bytes of non-byte slice")
	
	// Slice is always bigger than a word; assume flagIndir.
	// 切片总是比字(word)大; 假设flagIndir已经被设置值。
	return *(*[]byte)(v.ptr)


// runes returns v's underlying value.
// It panics if v's underlying value is not a slice of runes (int32s).
/**
 * runes返回v的底层值。如果v的底层值不是一小段符文(int32s),它将惊慌。
 * @param 
 * @return 
 **/
func (v Value) runes() []rune 
	v.mustBe(Slice)
	if v.typ.Elem().Kind() != Int32 
		panic("reflect.Value.Bytes of non-rune slice")
	
	// Slice is always bigger than a word; assume flagIndir.
	return *(*[]rune)(v.ptr)


// CanAddr reports whether the value's address can be obtained with Addr.
// Such values are called addressable. A value is addressable if it is
// an element of a slice, an element of an addressable array,
// a field of an addressable struct, or the result of dereferencing a pointer.
// If CanAddr returns false, calling Addr will panic.
/**
 * CanAddr报告是否可以通过Addr获取值的地址。
 * 这样的值称为可寻址的。 如果值是切片的元素,可寻址数组的元素,可寻址结构的字段或取消引用指针的结果,则该值是可寻址的。
 * 如果CanAddr返回false,则调用Addr会引起恐慌。
 **/
func (v Value) CanAddr() bool 
	return v.flag&flagAddr != 0


// CanSet reports whether the value of v can be changed.
// A Value can be changed only if it is addressable and was not
// obtained by the use of unexported struct fields.
// If CanSet returns false, calling Set or any type-specific
// setter (e.g., SetBool, SetInt) will panic.
/**
 * CanSet报告v的值是否可以更改。
 * 仅当值是可寻址的并且不是通过使用未导出的结构字段获得的,才可以更改它。
 * 如果CanSet返回false,则调用Set或任何特定于类型的setter(例如SetBool,SetInt)都会引起恐慌。
 * @param
 * @return
 **/
func (v Value) CanSet() bool 
	return v.flag&(flagAddr|flagRO) == flagAddr


// Call calls the function v with the input arguments in.
// For example, if len(in) == 3, v.Call(in) represents the Go call v(in[0], in[1], in[2]).
// Call panics if v's Kind is not Func.
// It returns the output results as Values.
// As in Go, each input argument must be assignable to the
// type of the function's corresponding input parameter.
// If v is a variadic function, Call creates the variadic slice parameter
// itself, copying in the corresponding values.
/**
 * Call使用输入参数调用函数v。
 * 例如,如果len(in)== 3,则v.Call(in)表示Go调用v(in[0], in[1], in[2])。
 * 如果v的Kind不是Func,则Call方法引起恐慌。
 * 将输出结果作为Value切片返回。
 * 和Go一样,每个输入参数必须可分配给函数相应输入参数的类型。
 * 如果v是可变参数函数,则Call会自己创建可变参数切片参数,并复制相应的值。
 * @param
 * @return
 **/
func (v Value) Call(in []Value) []Value 
	v.mustBe(Func)
	v.mustBeExported()
	return v.call("Call", in)


// CallSlice calls the variadic function v with the input arguments in,
// assigning the slice in[len(in)-1] to v's final variadic argument.
// For example, if len(in) == 3, v.CallSlice(in) represents the Go call v(in[0], in[1], in[2]...).
// CallSlice panics if v's Kind is not Func or if v is not variadic.
// It returns the output results as Values.
// As in Go, each input argument must be assignable to the
// type of the function's corresponding input parameter.
/**
 * CallSlice使用输入参数in调用可变参数函数v,将切片in [len(in)-1]分配给v的最终可变参数。
 * 例如,如果len(in) == 3,则v.CallSlice(in)表示Go调用v(in [0],in [1],in [2] ...)。
 * 如果v的Kind不是Func或v不是可变参数,则CallSlice会慌张。
 * 将输出结果作为Value切片返回。
 * 和Go一样,每个输入参数必须可分配给函数相应输入参数的类型。
 * @param
 * @return
 **/
func (v Value) CallSlice(in []Value) []Value 
	v.mustBe(Func)
	v.mustBeExported()
	return v.call("CallSlice", in)


var callGC bool // for testing; see TestCallMethodJump

/**
 * 真正的方法调用在这里
 * @param
 * @return
 **/
func (v Value) call(op string, in []Value) []Value 
	// Get function pointer, type.
	t := (*funcType)(unsafe.Pointer(v.typ))
	var (
		fn       unsafe.Pointer
		rcvr     Value
		rcvrtype *rtype
	)
	if v.flag&flagMethod != 0  // v是方法的接收者
		rcvr = v
		rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
	 else if v.flag&flagIndir != 0  // 有间接指针
		fn = *(*unsafe.Pointer)(v.ptr)
	 else 
		fn = v.ptr
	

	if fn == nil 
		panic("reflect.Value.Call: call of nil function")
	

	isSlice := op == "CallSlice"
	n := t.NumIn()
	if isSlice  // CallSlice方法
		if !t.IsVariadic()  // 必须要有可变参数
			panic("reflect: CallSlice of non-variadic function")
		
		if len(in) < n  // 参数不足
			panic("reflect: CallSlice with too few input arguments")
		
		if len(in) > n  // 参数过多
			panic("reflect: CallSlice with too many input arguments")
		
	 else 
		if t.IsVariadic()  // 有可变参数
			n--
		
		if len(in) < n 
			panic("reflect: Call with too few input arguments")
		
		if !t.IsVariadic() && len(in) > n 
			panicgo源码阅读——chan.go

go源码阅读——chan.go

[Go] gocron源码阅读-groutine与channel应用到信号捕获

golang channel源码阅读

golang channel源码阅读

BOM 浏览器对象模型