text/template 代码阅读: exec

目录

  • 版权
  • 前言
  • prepare
    • struct
      • struct 赋值是copy
    • pointer
      • nil pointer 本身有地址
      • 嵌套
      • calling a method on a nil pointer can work
    • interface
      • nil interface 有地址
      • 不能给interface 再定义方法
      • calling a method on a nil interface can't work
      • 赋值给interface 的、和type assertion 产生的, 都是copy
      • concrete
    • reflect.Value
      • concrete
      • zero Value
      • ValueOf nil
      • 一个可能的编译器bug
      • Value 嵌套的例子
      • Value 嵌套interface
  • exec.go
    • eval
      • evalPipeline
      • evalCommand
      • evalFieldNode
      • evalChainNode
      • evalFieldChain
      • evalField
      • evalVariableNode
      • walkRange
      • isTrue
    • call
      • evalCall
      • validateType
      • evalArg
      • evalEmptyInterface
      • indirect
      • printableValue
  • 补充
    • function 何时会执行, 何时不会?
    • 一个遗漏检查Value concrete 值为nil 的bug

版权

本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.csdn.net/big_cheng/article/details/131683131.

前言

go1.19

prepare

struct

struct 赋值是copy

type TS_S1 struct {
	I int
}

func ts_1() {
	s1 := TS_S1{1}
	var s2 TS_S1 = s1
	s1.I = 2
	println(s1.I, s2.I) // 2 1
}

pointer

nil pointer 本身有地址

// pointer (的值, 即其存储的地址) 和pointer(变量) 本身的地址 含义不同.
// (pointer 0x0 值代表指向nil.)
func tp_1() {
	var p *int = nil
	fmt.Printf("%p %v %p\n", p, p, &p) // 0x0  0xc00000a028

	var i = 3
	p = &i
	fmt.Printf("%p %v %p\n", p, p, &p) // 0xc00000e0d0 0xc00000e0d0 0xc00000a028 // (地址不变, 值变了)
}

嵌套

pointer 类型可嵌套:

func tp_1b() {
	i := 1
	pi := &i
	p := &pi
	fmt.Printf("%T\n", p) // **int
}

ref/spec#Method_declarations: “the receiver. Its type must be a defined type T or a pointer to a defined type T … A receiver base type cannot be a pointer or interface type”.

type TP_PI *int

// method receiver 的base type 不能是pointer.
// (pointer 可嵌套, 但嵌套后不能再定义方法 - 为了简单.)
// func (TP_PI) M1()  {}
// func (*TP_PI) M2() {}

上面2个方法定义的receiver base type 都是TP_PI - 是pointer 类型, 所以不许.

type MyInt int

func (*MyInt) M3() {}

上面receiver base type 是MyInt - 不是pointer 类型, 所以可以.

calling a method on a nil pointer can work

type TP_S2 struct {
	I int
}

func (*TP_S2) M2() {}

func tp_4() {
	var p *TP_S2
	p.M2()
	// println(p.I) - panic of nil
}

interface

nil interface 有地址

func ti_1() (err error) {
	fmt.Printf("addr %p => nil %t\n", &err, err == nil) // addr 0xc0000422e0 => nil true
	return nil
}

不能给interface 再定义方法

type TI_I1 interface {
	M1()
}

// (method receiver 的base type 不能是interface.)
// func (TI_I1) M2()  {}
// func (*TI_I1) M3() {}

calling a method on a nil interface can’t work

func ti_1c() {
	var i TI_I1
	i.M1() // panic of nil
}

赋值给interface 的、和type assertion 产生的, 都是copy

func (TS_S1) Read(p []byte) (int, error) { return len(p), nil }

func ti_2() {
	s := TS_S1{1}
	var r io.Reader = s
	s.I = 2
	s2 := r.(TS_S1)
	s2.I = 3
	println(s.I, (r.(TS_S1)).I, s2.I) // 2 1 3
}

concrete

// interface 内部包含一个concrete (值与类型).
// interface 赋值给interface, 实际赋的是concrete copy.
// 即concrete 不会是interface - 也即interface 不能嵌套interface.
func ti_3() {
	s := TS_S1{1}
	var r io.Reader = s
	var i any = r // (赋的不是r, 而是其concrete copy)
	s.I = 2
	println(s.I, (i.(TS_S1).I))           // 2 1
	fmt.Printf("%s\n", reflect.TypeOf(i)) // tmpl.TS_S1 (any 记录了concrete 的类型)
}

concrete 的值与类型:

// interface 的concrete:
//
//	值为untyped nil/interface nil 时, 没有类型.
//	值为其他nillable-typed nil 时, 有类型 - 这与reflect.TypeOf 行为一致.
func ti_4() {
	var i any = nil
	var r io.Reader = nil
	fmt.Printf("%t %t %t\n", // true
		reflect.TypeOf(i) == nil,
		reflect.TypeOf(r) == nil,
		reflect.TypeOf((io.Reader)(nil)) == nil,
	)

	fmt.Printf("%t %t %t %t/%t %t\n", // false
		reflect.TypeOf((chan int)(nil)) == nil,
		reflect.TypeOf((func() int)(nil)) == nil,
		reflect.TypeOf((map[string]int)(nil)) == nil,
		reflect.TypeOf((*int)(nil)) == nil,
		reflect.TypeOf((*any)(nil)) == nil,
		reflect.TypeOf(([]int)(nil)) == nil,
	)
}

reflect.Value

concrete

// Value 类似interface, 也是内部包含一个concrete (值与类型).
// 例如Value.Type() 实际是指该concrete 的类型.
func tr_1() {
	v := reflect.ValueOf(2)
	fmt.Printf("%s\n", v.Type()) // int
	println(v.Interface().(int)) // 2
}

zero Value

// Value invalid 代表从未设置过concrete.
//
// Zero(ValueType) 返回的value 设置了concrete = 另一个invalid 的value.
// 注: v.IsZero() 是针对concrete 而非v 自己.
//
// 另, 上述也表明: value 可嵌套value.
func tr_2() {
	var v reflect.Value                   // 或 v := reflect.Value{}
	fmt.Printf("valid %t\n", v.IsValid()) // (Invalid)

	typ := reflect.TypeOf((*reflect.Value)(nil)).Elem()
	fmt.Printf("%s\n", typ) // reflect.Value
	v = reflect.Zero(typ)
	// reflect.Value (struct): valid true zero true
	fmt.Printf("%s (%s): valid %t zero %t\n",
		v.Type(), v.Kind(), v.IsValid(), v.IsZero())
	//
	v = v.Interface().(reflect.Value)
	fmt.Printf("%t\n", v.IsValid()) // (Invalid)
}

(重要)

ValueOf nil

// ValueOf untyped nil/interface nil 结果invalid.
//
// 对其他nillable-typed nil 结果valid - 即concrete 有类型无值(为nil).
// (注: 包括chan/func/map/pointer/slice).
func tr_3() {
	// (Invalid)
	println(
		reflect.ValueOf(nil).IsValid(),
		reflect.ValueOf((any)(nil)).IsValid(),
		reflect.ValueOf((io.Reader)(nil)).IsValid(),
	)

	// (Valid)
	v := reflect.ValueOf((map[string]int)(nil))
	fmt.Printf("%t, %s\n", v.IsNil(), v.Type()) // true, map[string]int

	/* 注: Zero(interface) 是返回的value 的concrete = nil interface,
	       和ValueOf(nil interface) 结果不同.
	typ := reflect.TypeOf((*io.Reader)(nil)).Elem()
	v = reflect.Zero(typ)
	fmt.Printf("%t, %s\n", v.IsNil(), v.Type()) // true, io.Reader
	*/
}

另,

// TypeOf 返回nil 对应 ValueOf 返回invalid.
func tr_4() {
	t := reflect.TypeOf((any)(nil))
	println(t == nil) // true

	t = reflect.TypeOf((map[string]int)(nil))
	fmt.Printf("%s\n", t) // map[string]int
}

一个可能的编译器bug

// 可能是编译器bug: 改成用同一个变量v, 则打印 true - 不对!
func tr_elem() {
	i := reflect.Value{}
	v := reflect.ValueOf(&i)
	v = v.Elem() // 返回的v 的concrete 对应i
	v = v.Interface().(reflect.Value)
	println(v.IsValid()) // false
}

(Elem() 应该总是把目标对象包装在一个Value 里返回 - 在本例里就是Value 嵌Value.)

Value 嵌套的例子

// 如果map 包含, 返回valid Value - 其concrete 是另一个invalid Value.
func tr_mapIdx() {
	m := map[string]reflect.Value{
		"i1":  reflect.ValueOf(1),
		"foo": {},
	}
	vm := reflect.ValueOf(m)

	v := vm.MapIndex(reflect.ValueOf("foo"))
	println(v.IsValid()) // true
	v = v.Interface().(reflect.Value)
	println(v.IsValid()) // false
}

Value 嵌套interface

type TR_I1 interface {
	M1()
}

type TR_MAP map[string]any

func (TR_MAP) M1() {}

func tr_map2() {
	m := TR_MAP{"k1": 1}
	var tr TR_I1 = m
	var i any = tr // 实际赋的是m copy
	m["k1"] = 2

	vM := reflect.ValueOf(i)
	fmt.Printf("%s\n", vM.Type()) // tmpl.TR_MAP
	// vK1 的concrete 类型是any
	vK1 := vM.MapIndex(reflect.ValueOf("k1"))
	println(vK1.Kind().String(), vK1.Elem().Int()) // interface 2

	// vK1 的concrete (any) 的concrete: int/2
	i = vK1.Interface()
	println(i.(int)) // 2
}

(map 赋值, 其elements 实际还是同一份 - 并未copy.)

exec.go

eval

evalPipeline

func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value)

for _, cmd := range pipe.Cmds {
    value = s.evalCommand(dot, cmd, value)
pipeline 由一组cmd 串联而成, 依次执行, 前一个cmd 的结果value 作为后一个cmd 的追加参数.

evalCommand

func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value

cmd = arg0 arg1 arg2 …
例如: .Method1 .Key1 .Field2.Field3

arg0 如果是Field/Chain/Identifier/Pipe/Variable 节点, 则转到对应方法.
注: notAFunction 方法检查cmd 不是个方法调用: 只有arg0 且无final (== missingVal).

其他节点均不是方法调用: Bool/Dot/Nil/Number/String.

errorf 自己会panic, 后面再加panic 使意图更明确.

evalFieldNode

func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value

field = args[0].
args[1:] 和final 携带可能的其他参数, 因为field 可能是个方法如: .Method1 .Key1 123 nil.

直接转到evalFieldChain.

evalChainNode

func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value

ChainNode = FieldNode 前加一个(非Field/Identifier/Variable) 节点 - 一般这是一个pipe 节点: (pipe).Field1.Field2. 但不做假定, 调用通用的evalArg 求值:
pipe := s.evalArg(dot, nil, chain.Node)
(注: 类型传nil - 目前不知.)

再转到evalFieldChain (以pipe 为receiver).

evalFieldChain

func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value

.X.Y.Z arg1 arg2 … 形式. dot 用于arg, receiver 用于chain (.X.Y.Z).

chain 的各级存储在ident, 从首个到次末个(.X, .Y) 依次调用evalField 求值. chain 的前面各级肯定没有参数, 所以传nil 和missingVal. receiver 逐次更新.

最末ident (.Z) 求值时传递args 和final.

evalField

func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value

求receiver.fieldName 的值. 可能有arg (此时fieldName 是方法名) - 用dot 求其值.

如果receiver 是pointer 或interface, 连续去掉所有的pointer 和interface 返回最里层的concrete (注: interface 不能嵌套所以应该只有一层).
阻止"calling a method on a nil interface can’t work" 情况.

首先将fieldName 当作方法名来处理.
receiver 已经dereference.
因为待调用方法可能定义在&receiver 上, 再indirect 一次(在*T 上可以同时看到T 和*T 的方法), 但也阻止"不能给interface 再定义方法" 和"A receiver base type cannot be a pointer" 2种情况.
如果能找到该方法, 转到evalCall. 否则不是方法, 往下.

不是方法时hasArgs 应该为false.

  • receiver 是struct: 如果成功取到该字段的值但hasArgs 则报错 - 因为这里要直接返回字段值不论其是否方法, 所以不能有参数.
  • receiver 是map: 用fieldName 做key 取map 对应元素. 如果不存在则按option.missingKey 配置的方式处理.
  • receiver 是pointer: indirect 方法返回pointer 时应该isNil==true. 特殊处理如果pointer 是指向struct 且能找到fieldName 字段, 则改为报错"nil pointer evaluating …".

其他情况报错"can’t evaluate field …".

evalVariableNode

func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value

$x.Field1.Field2… 形式.

取出$x 的值, 再转到evalFieldChain.

walkRange

func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode)

evalPipeline 后indirect, 以防待遍历对象嵌套在Value 里面.

oneIteration:
因为evalPipeline 里已经定义了变量, 所以oneIteration 里可以直接setTopVar.

区分val 的类型: Array/Slice/Map/Chan 来遍历并调用oneIteration. 如果没有一条数据则再处理ElseList.

isTrue

func isTrue(val reflect.Value) (truth, ok bool)

在模板里一个reflect.Value val 是否为真. 不支持大类UnsafePointer. 对于数值是非0, 集合(array/map/slice/string) 是长度>0, nillable-type (chan/func/pointer/interface) 是非nil, struct 总是真.

call

evalCall

func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value

调用函数或方法fun, 名称为name - node (即args[0]) 的一部分.

首先检查fun 参数的个数(比较绕).

内置函数and/or 直接在这里完成处理. 见funcs.go, 这2个函数的参数类型是reflect.Value (即argType), 所以evalArg 求出的结果是Value 嵌套一个Value. .Interface().(reflect.Value) 取到嵌套的Value.
如果短路处理到final, final 已经有值, 只需再校验类型.

下面组装参数值数组(同样较绕).

最终调用funcs.go 的safeCall 完成函数调用.

注-unwrap: 因为Value.Call 返回[]Value, 如果被调用函数本身是返回Value, 则将返回Value 嵌套Value, unwrap 主要用在这种情况下剥离外层的Value.

validateType

func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value

检查value 可否赋给类型typ 的变量.

value Invalid 时返回Invalid、或nillable-type 的nil 值、或特殊地对struct (non-nillable) 仅支持reflect.Value 类型 - 返回Zero(ValueType) - 与后面evalArg 方法里一致.

value Valid 时, 额外自动转换:

  • value 是interface 且其concrete 能赋给typ, 则返回concrete
  • value 是pointer 且其base type 能赋给typ, 则返回其指向的对象(dereference 一次)
  • 指向value 类型的pointer 类型能赋给typ, 则返回指向value 的指针 (indirection 一次)

注意后2种dereference/indirection 只做一次, 不像在方法调用时持续做(见indirect 方法), 因为实践来看在这里一次即可.

evalArg

func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value

给出任一arg 节点n, 及其类型typ (可能nil), 求值. 有2种情况:

  • (pipe).Field1.Field2: 求pipe 值, typ nil
  • 函数/方法调用如.Method arg1 arg2: 求arg 值, typ non-nil

与evalCommand 方法较类似, 先处理可能带参数的节点类型(Field/Chain 等) 但增加类型dot/nil:

  • dot: 对 (.).Field1, typ nil, 所以不能放到后面(switch typ.Kind()) 处理
  • nil: 单个的nil 不能做cmd 节点 (见evalCommand 方法), 所以这是对函数调用求参数值, typ non-nil, 可以放到这里(switch arg := n.(type)) 处理

再switch typ.Kind() 处理(typ non-nil).
例如typ 大类是bool, 则evalBool 要求节点n 一定要是BoolNode.
增加any 和reflect.Value 大类的处理.

evalEmptyInterface

func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value

求函数调用的参数值, 参数类型any 或reflect.Value.

与evalArg 方法相比少了ChainNode - 因为在evalArg 前半段已处理.

indirect

func indirect(v reflect.Value) (rv reflect.Value, isNil bool)

循环dereference pointer/interface, 直至其指向的或concrete 是nil.

注意: 返回的isNil false 可能不准, 例如用一个nil slice 调用也返回false. 只有返回的isNil true 才是准的.

printableValue

func printableValue(v reflect.Value) (any, bool)

注-“return v.Interface()”: 如果v 嵌套interface 则返回该interface, fmt 对interface 会打印其concrete.

补充

function 何时会执行, 何时不会?

见evalField 方法. 如果receiver 上能找到该方法, 则会执行. 否则receiver 作为struct 拥有该方法(为field) 或作为map 拥有该方法(为elem) 都会直接返回而非执行该方法.
(ref/spec#Method_declarations: If the base type is a struct type, the non-blank method and field names must be distinct.)

例如TS_S1 类型定义Add 方法:

func (s TS_S1) Add(i ...int) int

则下面模板会执行该方法2次(.Key1 求值为TS_S1 对象):

a {{.Key1.Add .Key1.Add}} b

一个遗漏检查Value concrete 值为nil 的bug

funcs.go addValueFuncs 方法用于添加用户自定义函数:

func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
    for name, fn := range in {
        ......
        v := reflect.ValueOf(fn)
        if v.Kind() != reflect.Func { ...... }
        if !goodFunc(v.Type()) { ...... }
        out[name] = v
    }
}

这里检查了v.Kind() 和v.Type(), 但遗漏检查v.IsNil() - 因为func 类型是nillable-type, Value concrete 可以类型是func 但值为nil.

	t := template.New("P")
	t.Funcs(template.FuncMap{
		"foo": (func() int)(nil),
	})
	t.Parse("a {{call foo}} b")

上面模板可以Parse, 但执行时evalCall => safeCall => fun.Call(args) 会报错: call of nil function!

你可能感兴趣的:(golang)