251 lines · 6.1 KB · Go
RawDownload
package typeapply import (	"reflect"	"sync") var (	typeMutex sync.Mutex 	// tmap stores a map for every target type (the	// argument type of the function passed to Do).	// For each target type, we use this to cache	// information about how to traverse values	// of all types that have been passed to Do	// (and any types that they refer to).	tmap = make(map[reflect.Type]map[reflect.Type]*typeInfo)) // A traverserFunc implements Do for a particular// value type X and target type T. It assumes that x is of// type X and f is of type func(T).type traverserFunc func(f reflect.Value, x reflect.Value) type knowledge byte const (	dontKnow = knowledge(iota) // don't know yet	no                         // definite no.	yes                        // definite yes.) // typeInfo holds information on a value type with respect to a particular// target type.type typeInfo struct {	// Can this type contain instances of the target type?	// This can be dontKnow during execution of the canReach	// function	canReach knowledge 	// Is this type being currently visited?	visiting bool 	// A function that implements Do on this type.	trav traverserFunc} // Do calls the function f, which must be of the form func(T) for some// type T, on each publicly accessible member of the value x and recursively on// members of those. It will fail with infinite recursion if x is// cyclic. If a member of x is reachable in more than one way,// then f will be called for each way.func Do(f interface{}, x interface{}) {	fv := reflect.ValueOf(f)	ft := fv.Type()	if ft.NumIn() != 1 {		panic("function should take exactly one argument only")	}	if ft.NumOut() != 0 {		panic("function should have no return values")	}	xv := reflect.ValueOf(x)	xt := xv.Type() 	// t is the type that we're looking for.	t := ft.In(0) 	typeMutex.Lock()	m := tmap[t]	if m == nil {		m = make(map[reflect.Type]*typeInfo)		tmap[t] = m	}	trav := getTraverserFunc(m, t, xt)	typeMutex.Unlock()	if trav != nil {		trav(fv, xv)	}} // getTraverserFunc returns the traverserFunc for a given target type t// and and value type xt. It returns nil if xt cannot contain any// instance of t.func getTraverserFunc(m map[reflect.Type]*typeInfo, t, xt reflect.Type) traverserFunc {	needFunc := canReach(m, t, xt)	info := m[xt]	if info.trav != nil || needFunc == no {		return info.trav	}	// If the answer is "don't know", then it can only be	// be because we have a recursive type with no instances of t.	if needFunc == dontKnow {		info.canReach = no		return nil	}	// If we've actually found an instance of the type, then	// the traverserFunc calls the function on it.	if xt == t {		info.trav = func(fv reflect.Value, xv reflect.Value) {			fv.Call([]reflect.Value{xv})		}		return info.trav	}	// prime the typeInfo with a dummy function, so that	// getTraverserFunc returns non-nil when called recursively	// on types that can reach t. It will be reset before the	// end of the function, and the only way we can recurse	// is through a StructValue, which indirects through the typeInfo,	// so will use the eventual value.	info.trav = dummyTraverser	switch xt.Kind() {	case reflect.Ptr:		elemFunc := getTraverserFunc(m, t, xt.Elem())		info.trav = func(fv reflect.Value, xv reflect.Value) {			if y := xv; !y.IsNil() {				elemFunc(fv, y.Elem())			}		} 	case reflect.Struct:		n := xt.NumField()		type fieldFunc struct {			i    int			info *typeInfo		}		fields := make([]fieldFunc, n)		j := 0		for i := 0; i < n; i++ {			ft := xt.Field(i).Type			if getTraverserFunc(m, t, ft) != nil {				fields[j] = fieldFunc{i, m[ft]}				j++			}		}		fields = fields[0:j]		info.trav = func(fv reflect.Value, xv reflect.Value) {			y := xv			for _, f := range fields {				// indirect through info so that recursive types work ok.				f.info.trav(fv, y.Field(f.i))			}		} 	case reflect.Map:		travKey := getTraverserFunc(m, t, xt.Key())		travElem := getTraverserFunc(m, t, xt.Elem())		info.trav = func(fv reflect.Value, xv reflect.Value) {			y := xv			for _, key := range y.MapKeys() {				if travKey != nil {					travKey(fv, key)				}				if travElem != nil {					travElem(fv, y.MapIndex(key))				}			}		} 	case reflect.Array, reflect.Slice:		trav := getTraverserFunc(m, t, xt.Elem())		info.trav = func(fv reflect.Value, xv reflect.Value) {			y := xv			n := y.Len()			for i := 0; i < n; i++ {				trav(fv, y.Index(i))			}		} 	case reflect.Interface:		info.trav = func(fv reflect.Value, xv reflect.Value) {			y := xv			if y.IsNil() {				return			}			elem := y.Elem()			typeMutex.Lock()			trav := getTraverserFunc(m, t, elem.Type())			typeMutex.Unlock()			if trav != nil {				trav(fv, elem)			}		}	default:		panic("unexpected type reached: " + xt.String())	}	return info.trav} // canReach returns whether t can be reached from xt.func canReach(m map[reflect.Type]*typeInfo, t, xt reflect.Type) knowledge {	if info := m[xt]; info != nil {		if info.visiting || info.canReach != dontKnow {			return info.canReach		}	}	info := new(typeInfo)	m[xt] = info	if t == xt {		info.canReach = yes		return yes	}	info.visiting = true	switch xt.Kind() {	case reflect.Ptr:		info.canReach = canReach(m, t, xt.Elem()) 	case reflect.Struct:		for i := 0; i < xt.NumField(); i++ {			f := xt.Field(i)			// ignore unexported members.			if f.PkgPath == "" {				info.canReach = or(canReach(m, t, f.Type), info.canReach)			}		} 	case reflect.Map:		info.canReach = canReach(m, t, xt.Key())		info.canReach = or(canReach(m, t, xt.Elem()), info.canReach) 	case reflect.Array:		info.canReach = canReach(m, t, xt.Elem()) 	case reflect.Slice:		info.canReach = canReach(m, t, xt.Elem()) 	case reflect.Interface:		info.canReach = yes	}	info.visiting = false	return info.canReach} func dummyTraverser(fv reflect.Value, xv reflect.Value) {	panic("dummyTraverser called")} func or(a, b knowledge) knowledge {	switch {	case a == yes || b == yes:		return yes	case a == dontKnow || b == dontKnow:		return dontKnow	}	return no} // Is this an exported - upper case - name?//func isExported(name string) bool {//	rune, _ := utf8.DecodeRuneInString(name)//	return unicode.IsUpper(rune)//}