1976 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			1976 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2013 The Go Authors. All rights reserved.
 | ||
| // Use of this source code is governed by a BSD-style
 | ||
| // license that can be found in the LICENSE file.
 | ||
| 
 | ||
| package pointer
 | ||
| 
 | ||
| // This file implements the generation and resolution rules for
 | ||
| // constraints arising from the use of reflection in the target
 | ||
| // program.  See doc.go for explanation of the representation.
 | ||
| //
 | ||
| // For consistency, the names of all parameters match those of the
 | ||
| // actual functions in the "reflect" package.
 | ||
| //
 | ||
| // To avoid proliferation of equivalent labels, intrinsics should
 | ||
| // memoize as much as possible, like TypeOf and Zero do for their
 | ||
| // tagged objects.
 | ||
| //
 | ||
| // TODO(adonovan): this file is rather subtle.  Explain how we derive
 | ||
| // the implementation of each reflect operator from its spec,
 | ||
| // including the subtleties of reflect.flag{Addr,RO,Indir}.
 | ||
| // [Hint: our implementation is as if reflect.flagIndir was always
 | ||
| // true, i.e. reflect.Values are pointers to tagged objects, there is
 | ||
| // no inline allocation optimization; and indirect tagged objects (not
 | ||
| // yet implemented) correspond to reflect.Values with
 | ||
| // reflect.flagAddr.]
 | ||
| // A picture would help too.
 | ||
| //
 | ||
| // TODO(adonovan): try factoring up the common parts of the majority of
 | ||
| // these constraints that are single input, single output.
 | ||
| 
 | ||
| import (
 | ||
| 	"fmt"
 | ||
| 	"go/constant"
 | ||
| 	"go/types"
 | ||
| 	"reflect"
 | ||
| 
 | ||
| 	"golang.org/x/tools/go/ssa"
 | ||
| )
 | ||
| 
 | ||
| func init() {
 | ||
| 	for name, fn := range map[string]intrinsic{
 | ||
| 		// reflect.Value methods.
 | ||
| 		"(reflect.Value).Addr":            ext۰reflect۰Value۰Addr,
 | ||
| 		"(reflect.Value).Bool":            ext۰NoEffect,
 | ||
| 		"(reflect.Value).Bytes":           ext۰reflect۰Value۰Bytes,
 | ||
| 		"(reflect.Value).Call":            ext۰reflect۰Value۰Call,
 | ||
| 		"(reflect.Value).CallSlice":       ext۰reflect۰Value۰CallSlice,
 | ||
| 		"(reflect.Value).CanAddr":         ext۰NoEffect,
 | ||
| 		"(reflect.Value).CanInterface":    ext۰NoEffect,
 | ||
| 		"(reflect.Value).CanSet":          ext۰NoEffect,
 | ||
| 		"(reflect.Value).Cap":             ext۰NoEffect,
 | ||
| 		"(reflect.Value).Close":           ext۰NoEffect,
 | ||
| 		"(reflect.Value).Complex":         ext۰NoEffect,
 | ||
| 		"(reflect.Value).Convert":         ext۰reflect۰Value۰Convert,
 | ||
| 		"(reflect.Value).Elem":            ext۰reflect۰Value۰Elem,
 | ||
| 		"(reflect.Value).Field":           ext۰reflect۰Value۰Field,
 | ||
| 		"(reflect.Value).FieldByIndex":    ext۰reflect۰Value۰FieldByIndex,
 | ||
| 		"(reflect.Value).FieldByName":     ext۰reflect۰Value۰FieldByName,
 | ||
| 		"(reflect.Value).FieldByNameFunc": ext۰reflect۰Value۰FieldByNameFunc,
 | ||
| 		"(reflect.Value).Float":           ext۰NoEffect,
 | ||
| 		"(reflect.Value).Index":           ext۰reflect۰Value۰Index,
 | ||
| 		"(reflect.Value).Int":             ext۰NoEffect,
 | ||
| 		"(reflect.Value).Interface":       ext۰reflect۰Value۰Interface,
 | ||
| 		"(reflect.Value).InterfaceData":   ext۰NoEffect,
 | ||
| 		"(reflect.Value).IsNil":           ext۰NoEffect,
 | ||
| 		"(reflect.Value).IsValid":         ext۰NoEffect,
 | ||
| 		"(reflect.Value).Kind":            ext۰NoEffect,
 | ||
| 		"(reflect.Value).Len":             ext۰NoEffect,
 | ||
| 		"(reflect.Value).MapIndex":        ext۰reflect۰Value۰MapIndex,
 | ||
| 		"(reflect.Value).MapKeys":         ext۰reflect۰Value۰MapKeys,
 | ||
| 		"(reflect.Value).Method":          ext۰reflect۰Value۰Method,
 | ||
| 		"(reflect.Value).MethodByName":    ext۰reflect۰Value۰MethodByName,
 | ||
| 		"(reflect.Value).NumField":        ext۰NoEffect,
 | ||
| 		"(reflect.Value).NumMethod":       ext۰NoEffect,
 | ||
| 		"(reflect.Value).OverflowComplex": ext۰NoEffect,
 | ||
| 		"(reflect.Value).OverflowFloat":   ext۰NoEffect,
 | ||
| 		"(reflect.Value).OverflowInt":     ext۰NoEffect,
 | ||
| 		"(reflect.Value).OverflowUint":    ext۰NoEffect,
 | ||
| 		"(reflect.Value).Pointer":         ext۰NoEffect,
 | ||
| 		"(reflect.Value).Recv":            ext۰reflect۰Value۰Recv,
 | ||
| 		"(reflect.Value).Send":            ext۰reflect۰Value۰Send,
 | ||
| 		"(reflect.Value).Set":             ext۰reflect۰Value۰Set,
 | ||
| 		"(reflect.Value).SetBool":         ext۰NoEffect,
 | ||
| 		"(reflect.Value).SetBytes":        ext۰reflect۰Value۰SetBytes,
 | ||
| 		"(reflect.Value).SetComplex":      ext۰NoEffect,
 | ||
| 		"(reflect.Value).SetFloat":        ext۰NoEffect,
 | ||
| 		"(reflect.Value).SetInt":          ext۰NoEffect,
 | ||
| 		"(reflect.Value).SetLen":          ext۰NoEffect,
 | ||
| 		"(reflect.Value).SetMapIndex":     ext۰reflect۰Value۰SetMapIndex,
 | ||
| 		"(reflect.Value).SetPointer":      ext۰reflect۰Value۰SetPointer,
 | ||
| 		"(reflect.Value).SetString":       ext۰NoEffect,
 | ||
| 		"(reflect.Value).SetUint":         ext۰NoEffect,
 | ||
| 		"(reflect.Value).Slice":           ext۰reflect۰Value۰Slice,
 | ||
| 		"(reflect.Value).String":          ext۰NoEffect,
 | ||
| 		"(reflect.Value).TryRecv":         ext۰reflect۰Value۰Recv,
 | ||
| 		"(reflect.Value).TrySend":         ext۰reflect۰Value۰Send,
 | ||
| 		"(reflect.Value).Type":            ext۰NoEffect,
 | ||
| 		"(reflect.Value).Uint":            ext۰NoEffect,
 | ||
| 		"(reflect.Value).UnsafeAddr":      ext۰NoEffect,
 | ||
| 
 | ||
| 		// Standalone reflect.* functions.
 | ||
| 		"reflect.Append":      ext۰reflect۰Append,
 | ||
| 		"reflect.AppendSlice": ext۰reflect۰AppendSlice,
 | ||
| 		"reflect.Copy":        ext۰reflect۰Copy,
 | ||
| 		"reflect.ChanOf":      ext۰reflect۰ChanOf,
 | ||
| 		"reflect.DeepEqual":   ext۰NoEffect,
 | ||
| 		"reflect.Indirect":    ext۰reflect۰Indirect,
 | ||
| 		"reflect.MakeChan":    ext۰reflect۰MakeChan,
 | ||
| 		"reflect.MakeFunc":    ext۰reflect۰MakeFunc,
 | ||
| 		"reflect.MakeMap":     ext۰reflect۰MakeMap,
 | ||
| 		"reflect.MakeSlice":   ext۰reflect۰MakeSlice,
 | ||
| 		"reflect.MapOf":       ext۰reflect۰MapOf,
 | ||
| 		"reflect.New":         ext۰reflect۰New,
 | ||
| 		"reflect.NewAt":       ext۰reflect۰NewAt,
 | ||
| 		"reflect.PtrTo":       ext۰reflect۰PtrTo,
 | ||
| 		"reflect.Select":      ext۰reflect۰Select,
 | ||
| 		"reflect.SliceOf":     ext۰reflect۰SliceOf,
 | ||
| 		"reflect.TypeOf":      ext۰reflect۰TypeOf,
 | ||
| 		"reflect.ValueOf":     ext۰reflect۰ValueOf,
 | ||
| 		"reflect.Zero":        ext۰reflect۰Zero,
 | ||
| 		"reflect.init":        ext۰NoEffect,
 | ||
| 
 | ||
| 		// *reflect.rtype methods
 | ||
| 		"(*reflect.rtype).Align":           ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).AssignableTo":    ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).Bits":            ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).ChanDir":         ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).ConvertibleTo":   ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).Elem":            ext۰reflect۰rtype۰Elem,
 | ||
| 		"(*reflect.rtype).Field":           ext۰reflect۰rtype۰Field,
 | ||
| 		"(*reflect.rtype).FieldAlign":      ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).FieldByIndex":    ext۰reflect۰rtype۰FieldByIndex,
 | ||
| 		"(*reflect.rtype).FieldByName":     ext۰reflect۰rtype۰FieldByName,
 | ||
| 		"(*reflect.rtype).FieldByNameFunc": ext۰reflect۰rtype۰FieldByNameFunc,
 | ||
| 		"(*reflect.rtype).Implements":      ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).In":              ext۰reflect۰rtype۰In,
 | ||
| 		"(*reflect.rtype).IsVariadic":      ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).Key":             ext۰reflect۰rtype۰Key,
 | ||
| 		"(*reflect.rtype).Kind":            ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).Len":             ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).Method":          ext۰reflect۰rtype۰Method,
 | ||
| 		"(*reflect.rtype).MethodByName":    ext۰reflect۰rtype۰MethodByName,
 | ||
| 		"(*reflect.rtype).Name":            ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).NumField":        ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).NumIn":           ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).NumMethod":       ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).NumOut":          ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).Out":             ext۰reflect۰rtype۰Out,
 | ||
| 		"(*reflect.rtype).PkgPath":         ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).Size":            ext۰NoEffect,
 | ||
| 		"(*reflect.rtype).String":          ext۰NoEffect,
 | ||
| 	} {
 | ||
| 		intrinsicsByName[name] = fn
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // -------------------- (reflect.Value) --------------------
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func (Value).Bytes() Value ----------
 | ||
| 
 | ||
| // result = v.Bytes()
 | ||
| type rVBytesConstraint struct {
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVBytesConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVBytesConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rVBytes.result")
 | ||
| }
 | ||
| func (c *rVBytesConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVBytesConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVBytesConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, slice, indirect := a.taggedValue(vObj)
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		tSlice, ok := tDyn.Underlying().(*types.Slice)
 | ||
| 		if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) {
 | ||
| 			if a.onlineCopy(c.result, slice) {
 | ||
| 				changed = true
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Bytes(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rVBytesConstraint{
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (Value).Call(in []Value) []Value ----------
 | ||
| 
 | ||
| // result = v.Call(in)
 | ||
| type rVCallConstraint struct {
 | ||
| 	cgn       *cgnode
 | ||
| 	targets   nodeid // (indirect)
 | ||
| 	v         nodeid // (ptr)
 | ||
| 	arg       nodeid // = in[*]
 | ||
| 	result    nodeid // (indirect)
 | ||
| 	dotdotdot bool   // interpret last arg as a "..." slice
 | ||
| }
 | ||
| 
 | ||
| func (c *rVCallConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVCallConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.targets), "rVCall.targets")
 | ||
| 	h.markIndirect(onodeid(c.result), "rVCall.result")
 | ||
| }
 | ||
| func (c *rVCallConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.targets = mapping[c.targets]
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.arg = mapping[c.arg]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVCallConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.Call(n%d)", c.result, c.v, c.arg)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVCallConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	if c.targets == 0 {
 | ||
| 		panic("no targets")
 | ||
| 	}
 | ||
| 
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, fn, indirect := a.taggedValue(vObj)
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		tSig, ok := tDyn.Underlying().(*types.Signature)
 | ||
| 		if !ok {
 | ||
| 			continue // not a function
 | ||
| 		}
 | ||
| 		if tSig.Recv() != nil {
 | ||
| 			panic(tSig) // TODO(adonovan): rethink when we implement Method()
 | ||
| 		}
 | ||
| 
 | ||
| 		// Add dynamic call target.
 | ||
| 		if a.onlineCopy(c.targets, fn) {
 | ||
| 			a.addWork(c.targets)
 | ||
| 			// TODO(adonovan): is 'else continue' a sound optimisation here?
 | ||
| 		}
 | ||
| 
 | ||
| 		// Allocate a P/R block.
 | ||
| 		tParams := tSig.Params()
 | ||
| 		tResults := tSig.Results()
 | ||
| 		params := a.addNodes(tParams, "rVCall.params")
 | ||
| 		results := a.addNodes(tResults, "rVCall.results")
 | ||
| 
 | ||
| 		// Make a dynamic call to 'fn'.
 | ||
| 		a.store(fn, params, 1, a.sizeof(tParams))
 | ||
| 		a.load(results, fn, 1+a.sizeof(tParams), a.sizeof(tResults))
 | ||
| 
 | ||
| 		// Populate P by type-asserting each actual arg (all merged in c.arg).
 | ||
| 		for i, n := 0, tParams.Len(); i < n; i++ {
 | ||
| 			T := tParams.At(i).Type()
 | ||
| 			a.typeAssert(T, params, c.arg, false)
 | ||
| 			params += nodeid(a.sizeof(T))
 | ||
| 		}
 | ||
| 
 | ||
| 		// Use R by tagging and copying each actual result to c.result.
 | ||
| 		for i, n := 0, tResults.Len(); i < n; i++ {
 | ||
| 			T := tResults.At(i).Type()
 | ||
| 			// Convert from an arbitrary type to a reflect.Value
 | ||
| 			// (like MakeInterface followed by reflect.ValueOf).
 | ||
| 			if isInterface(T) {
 | ||
| 				// (don't tag)
 | ||
| 				if a.onlineCopy(c.result, results) {
 | ||
| 					changed = true
 | ||
| 				}
 | ||
| 			} else {
 | ||
| 				obj := a.makeTagged(T, c.cgn, nil)
 | ||
| 				a.onlineCopyN(obj+1, results, a.sizeof(T))
 | ||
| 				if a.addLabel(c.result, obj) { // (true)
 | ||
| 					changed = true
 | ||
| 				}
 | ||
| 			}
 | ||
| 			results += nodeid(a.sizeof(T))
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // Common code for direct (inlined) and indirect calls to (reflect.Value).Call.
 | ||
| func reflectCallImpl(a *analysis, cgn *cgnode, site *callsite, recv, arg nodeid, dotdotdot bool) nodeid {
 | ||
| 	// Allocate []reflect.Value array for the result.
 | ||
| 	ret := a.nextNode()
 | ||
| 	a.addNodes(types.NewArray(a.reflectValueObj.Type(), 1), "rVCall.ret")
 | ||
| 	a.endObject(ret, cgn, nil)
 | ||
| 
 | ||
| 	// pts(targets) will be the set of possible call targets.
 | ||
| 	site.targets = a.addOneNode(tInvalid, "rvCall.targets", nil)
 | ||
| 
 | ||
| 	// All arguments are merged since they arrive in a slice.
 | ||
| 	argelts := a.addOneNode(a.reflectValueObj.Type(), "rVCall.args", nil)
 | ||
| 	a.load(argelts, arg, 1, 1) // slice elements
 | ||
| 
 | ||
| 	a.addConstraint(&rVCallConstraint{
 | ||
| 		cgn:       cgn,
 | ||
| 		targets:   site.targets,
 | ||
| 		v:         recv,
 | ||
| 		arg:       argelts,
 | ||
| 		result:    ret + 1, // results go into elements of ret
 | ||
| 		dotdotdot: dotdotdot,
 | ||
| 	})
 | ||
| 	return ret
 | ||
| }
 | ||
| 
 | ||
| func reflectCall(a *analysis, cgn *cgnode, dotdotdot bool) {
 | ||
| 	// This is the shared contour implementation of (reflect.Value).Call
 | ||
| 	// and CallSlice, as used by indirect calls (rare).
 | ||
| 	// Direct calls are inlined in gen.go, eliding the
 | ||
| 	// intermediate cgnode for Call.
 | ||
| 	site := new(callsite)
 | ||
| 	cgn.sites = append(cgn.sites, site)
 | ||
| 	recv := a.funcParams(cgn.obj)
 | ||
| 	arg := recv + 1
 | ||
| 	ret := reflectCallImpl(a, cgn, site, recv, arg, dotdotdot)
 | ||
| 	a.addressOf(cgn.fn.Signature.Results().At(0).Type(), a.funcResults(cgn.obj), ret)
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Call(a *analysis, cgn *cgnode) {
 | ||
| 	reflectCall(a, cgn, false)
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) {
 | ||
| 	// TODO(adonovan): implement.  Also, inline direct calls in gen.go too.
 | ||
| 	if false {
 | ||
| 		reflectCall(a, cgn, true)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func (Value).Elem() Value ----------
 | ||
| 
 | ||
| // result = v.Elem()
 | ||
| type rVElemConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVElemConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVElemConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rVElem.result")
 | ||
| }
 | ||
| func (c *rVElemConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVElemConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVElemConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, payload, indirect := a.taggedValue(vObj)
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		switch t := tDyn.Underlying().(type) {
 | ||
| 		case *types.Interface:
 | ||
| 			if a.onlineCopy(c.result, payload) {
 | ||
| 				changed = true
 | ||
| 			}
 | ||
| 
 | ||
| 		case *types.Pointer:
 | ||
| 			obj := a.makeTagged(t.Elem(), c.cgn, nil)
 | ||
| 			a.load(obj+1, payload, 0, a.sizeof(t.Elem()))
 | ||
| 			if a.addLabel(c.result, obj) {
 | ||
| 				changed = true
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rVElemConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Field(a *analysis, cgn *cgnode)           {} // TODO(adonovan)
 | ||
| func ext۰reflect۰Value۰FieldByIndex(a *analysis, cgn *cgnode)    {} // TODO(adonovan)
 | ||
| func ext۰reflect۰Value۰FieldByName(a *analysis, cgn *cgnode)     {} // TODO(adonovan)
 | ||
| func ext۰reflect۰Value۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func (Value).Index() Value ----------
 | ||
| 
 | ||
| // result = v.Index()
 | ||
| type rVIndexConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVIndexConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVIndexConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rVIndex.result")
 | ||
| }
 | ||
| func (c *rVIndexConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVIndexConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVIndexConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, payload, indirect := a.taggedValue(vObj)
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		var res nodeid
 | ||
| 		switch t := tDyn.Underlying().(type) {
 | ||
| 		case *types.Array:
 | ||
| 			res = a.makeTagged(t.Elem(), c.cgn, nil)
 | ||
| 			a.onlineCopyN(res+1, payload+1, a.sizeof(t.Elem()))
 | ||
| 
 | ||
| 		case *types.Slice:
 | ||
| 			res = a.makeTagged(t.Elem(), c.cgn, nil)
 | ||
| 			a.load(res+1, payload, 1, a.sizeof(t.Elem()))
 | ||
| 
 | ||
| 		case *types.Basic:
 | ||
| 			if t.Kind() == types.String {
 | ||
| 				res = a.makeTagged(types.Typ[types.Rune], c.cgn, nil)
 | ||
| 			}
 | ||
| 		}
 | ||
| 		if res != 0 && a.addLabel(c.result, res) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rVIndexConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (Value).Interface() Value ----------
 | ||
| 
 | ||
| // result = v.Interface()
 | ||
| type rVInterfaceConstraint struct {
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVInterfaceConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVInterfaceConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rVInterface.result")
 | ||
| }
 | ||
| func (c *rVInterfaceConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVInterfaceConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVInterfaceConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, payload, indirect := a.taggedValue(vObj)
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		if isInterface(tDyn) {
 | ||
| 			if a.onlineCopy(c.result, payload) {
 | ||
| 				a.addWork(c.result)
 | ||
| 			}
 | ||
| 		} else {
 | ||
| 			if a.addLabel(c.result, vObj) {
 | ||
| 				changed = true
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rVInterfaceConstraint{
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (Value).MapIndex(Value) Value ----------
 | ||
| 
 | ||
| // result = v.MapIndex(_)
 | ||
| type rVMapIndexConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVMapIndexConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVMapIndexConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rVMapIndex.result")
 | ||
| }
 | ||
| func (c *rVMapIndexConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVMapIndexConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVMapIndexConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, m, indirect := a.taggedValue(vObj)
 | ||
| 		tMap, _ := tDyn.Underlying().(*types.Map)
 | ||
| 		if tMap == nil {
 | ||
| 			continue // not a map
 | ||
| 		}
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		obj := a.makeTagged(tMap.Elem(), c.cgn, nil)
 | ||
| 		a.load(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
 | ||
| 		if a.addLabel(c.result, obj) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rVMapIndexConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (Value).MapKeys() []Value ----------
 | ||
| 
 | ||
| // result = v.MapKeys()
 | ||
| type rVMapKeysConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVMapKeysConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVMapKeysConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rVMapKeys.result")
 | ||
| }
 | ||
| func (c *rVMapKeysConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVMapKeysConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVMapKeysConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, m, indirect := a.taggedValue(vObj)
 | ||
| 		tMap, _ := tDyn.Underlying().(*types.Map)
 | ||
| 		if tMap == nil {
 | ||
| 			continue // not a map
 | ||
| 		}
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		kObj := a.makeTagged(tMap.Key(), c.cgn, nil)
 | ||
| 		a.load(kObj+1, m, 0, a.sizeof(tMap.Key()))
 | ||
| 		if a.addLabel(c.result, kObj) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰MapKeys(a *analysis, cgn *cgnode) {
 | ||
| 	// Allocate an array for the result.
 | ||
| 	obj := a.nextNode()
 | ||
| 	T := types.NewSlice(a.reflectValueObj.Type())
 | ||
| 	a.addNodes(sliceToArray(T), "reflect.MapKeys result")
 | ||
| 	a.endObject(obj, cgn, nil)
 | ||
| 	a.addressOf(T, a.funcResults(cgn.obj), obj)
 | ||
| 
 | ||
| 	a.addConstraint(&rVMapKeysConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: obj + 1, // result is stored in array elems
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode)       {} // TODO(adonovan)
 | ||
| func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func (Value).Recv(Value) Value ----------
 | ||
| 
 | ||
| // result, _ = v.Recv()
 | ||
| type rVRecvConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVRecvConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVRecvConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rVRecv.result")
 | ||
| }
 | ||
| func (c *rVRecvConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVRecvConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVRecvConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, ch, indirect := a.taggedValue(vObj)
 | ||
| 		tChan, _ := tDyn.Underlying().(*types.Chan)
 | ||
| 		if tChan == nil {
 | ||
| 			continue // not a channel
 | ||
| 		}
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		tElem := tChan.Elem()
 | ||
| 		elemObj := a.makeTagged(tElem, c.cgn, nil)
 | ||
| 		a.load(elemObj+1, ch, 0, a.sizeof(tElem))
 | ||
| 		if a.addLabel(c.result, elemObj) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Recv(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rVRecvConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (Value).Send(Value) ----------
 | ||
| 
 | ||
| // v.Send(x)
 | ||
| type rVSendConstraint struct {
 | ||
| 	cgn *cgnode
 | ||
| 	v   nodeid // (ptr)
 | ||
| 	x   nodeid
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSendConstraint) ptr() nodeid   { return c.v }
 | ||
| func (c *rVSendConstraint) presolve(*hvn) {}
 | ||
| func (c *rVSendConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.x = mapping[c.x]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSendConstraint) String() string {
 | ||
| 	return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSendConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, ch, indirect := a.taggedValue(vObj)
 | ||
| 		tChan, _ := tDyn.Underlying().(*types.Chan)
 | ||
| 		if tChan == nil {
 | ||
| 			continue // not a channel
 | ||
| 		}
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		// Extract x's payload to xtmp, then store to channel.
 | ||
| 		tElem := tChan.Elem()
 | ||
| 		xtmp := a.addNodes(tElem, "Send.xtmp")
 | ||
| 		a.typeAssert(tElem, xtmp, c.x, false)
 | ||
| 		a.store(ch, xtmp, 0, a.sizeof(tElem))
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Send(a *analysis, cgn *cgnode) {
 | ||
| 	params := a.funcParams(cgn.obj)
 | ||
| 	a.addConstraint(&rVSendConstraint{
 | ||
| 		cgn: cgn,
 | ||
| 		v:   params,
 | ||
| 		x:   params + 1,
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func (Value).SetBytes(x []byte) ----------
 | ||
| 
 | ||
| // v.SetBytes(x)
 | ||
| type rVSetBytesConstraint struct {
 | ||
| 	cgn *cgnode
 | ||
| 	v   nodeid // (ptr)
 | ||
| 	x   nodeid
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSetBytesConstraint) ptr() nodeid   { return c.v }
 | ||
| func (c *rVSetBytesConstraint) presolve(*hvn) {}
 | ||
| func (c *rVSetBytesConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.x = mapping[c.x]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSetBytesConstraint) String() string {
 | ||
| 	return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSetBytesConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, slice, indirect := a.taggedValue(vObj)
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		tSlice, ok := tDyn.Underlying().(*types.Slice)
 | ||
| 		if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) {
 | ||
| 			if a.onlineCopy(slice, c.x) {
 | ||
| 				a.addWork(slice)
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) {
 | ||
| 	params := a.funcParams(cgn.obj)
 | ||
| 	a.addConstraint(&rVSetBytesConstraint{
 | ||
| 		cgn: cgn,
 | ||
| 		v:   params,
 | ||
| 		x:   params + 1,
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (Value).SetMapIndex(k Value, v Value) ----------
 | ||
| 
 | ||
| // v.SetMapIndex(key, val)
 | ||
| type rVSetMapIndexConstraint struct {
 | ||
| 	cgn *cgnode
 | ||
| 	v   nodeid // (ptr)
 | ||
| 	key nodeid
 | ||
| 	val nodeid
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSetMapIndexConstraint) ptr() nodeid   { return c.v }
 | ||
| func (c *rVSetMapIndexConstraint) presolve(*hvn) {}
 | ||
| func (c *rVSetMapIndexConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.key = mapping[c.key]
 | ||
| 	c.val = mapping[c.val]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSetMapIndexConstraint) String() string {
 | ||
| 	return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSetMapIndexConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, m, indirect := a.taggedValue(vObj)
 | ||
| 		tMap, _ := tDyn.Underlying().(*types.Map)
 | ||
| 		if tMap == nil {
 | ||
| 			continue // not a map
 | ||
| 		}
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		keysize := a.sizeof(tMap.Key())
 | ||
| 
 | ||
| 		// Extract key's payload to keytmp, then store to map key.
 | ||
| 		keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp")
 | ||
| 		a.typeAssert(tMap.Key(), keytmp, c.key, false)
 | ||
| 		a.store(m, keytmp, 0, keysize)
 | ||
| 
 | ||
| 		// Extract val's payload to vtmp, then store to map value.
 | ||
| 		valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp")
 | ||
| 		a.typeAssert(tMap.Elem(), valtmp, c.val, false)
 | ||
| 		a.store(m, valtmp, keysize, a.sizeof(tMap.Elem()))
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) {
 | ||
| 	params := a.funcParams(cgn.obj)
 | ||
| 	a.addConstraint(&rVSetMapIndexConstraint{
 | ||
| 		cgn: cgn,
 | ||
| 		v:   params,
 | ||
| 		key: params + 1,
 | ||
| 		val: params + 2,
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰SetPointer(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func (Value).Slice(v Value, i, j int) Value ----------
 | ||
| 
 | ||
| // result = v.Slice(_, _)
 | ||
| type rVSliceConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSliceConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *rVSliceConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rVSlice.result")
 | ||
| }
 | ||
| func (c *rVSliceConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSliceConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *rVSliceConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, payload, indirect := a.taggedValue(vObj)
 | ||
| 		if indirect {
 | ||
| 			// TODO(adonovan): we'll need to implement this
 | ||
| 			// when we start creating indirect tagged objects.
 | ||
| 			panic("indirect tagged object")
 | ||
| 		}
 | ||
| 
 | ||
| 		var res nodeid
 | ||
| 		switch t := tDyn.Underlying().(type) {
 | ||
| 		case *types.Pointer:
 | ||
| 			if tArr, ok := t.Elem().Underlying().(*types.Array); ok {
 | ||
| 				// pointer to array
 | ||
| 				res = a.makeTagged(types.NewSlice(tArr.Elem()), c.cgn, nil)
 | ||
| 				if a.onlineCopy(res+1, payload) {
 | ||
| 					a.addWork(res + 1)
 | ||
| 				}
 | ||
| 			}
 | ||
| 
 | ||
| 		case *types.Array:
 | ||
| 			// TODO(adonovan): implement addressable
 | ||
| 			// arrays when we do indirect tagged objects.
 | ||
| 
 | ||
| 		case *types.Slice:
 | ||
| 			res = vObj
 | ||
| 
 | ||
| 		case *types.Basic:
 | ||
| 			if t == types.Typ[types.String] {
 | ||
| 				res = vObj
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if res != 0 && a.addLabel(c.result, res) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rVSliceConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // -------------------- Standalone reflect functions --------------------
 | ||
| 
 | ||
| func ext۰reflect۰Append(a *analysis, cgn *cgnode)      {} // TODO(adonovan)
 | ||
| func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| func ext۰reflect۰Copy(a *analysis, cgn *cgnode)        {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func ChanOf(ChanDir, Type) Type ----------
 | ||
| 
 | ||
| // result = ChanOf(dir, t)
 | ||
| type reflectChanOfConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	t      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| 	dirs   []types.ChanDir
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectChanOfConstraint) ptr() nodeid { return c.t }
 | ||
| func (c *reflectChanOfConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectChanOf.result")
 | ||
| }
 | ||
| func (c *reflectChanOfConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.t = mapping[c.t]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectChanOfConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectChanOfConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		tObj := nodeid(x)
 | ||
| 		T := a.rtypeTaggedValue(tObj)
 | ||
| 
 | ||
| 		if typeTooHigh(T) {
 | ||
| 			continue
 | ||
| 		}
 | ||
| 
 | ||
| 		for _, dir := range c.dirs {
 | ||
| 			if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) {
 | ||
| 				changed = true
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // dirMap maps reflect.ChanDir to the set of channel types generated by ChanOf.
 | ||
| var dirMap = [...][]types.ChanDir{
 | ||
| 	0:               {types.SendOnly, types.RecvOnly, types.SendRecv}, // unknown
 | ||
| 	reflect.RecvDir: {types.RecvOnly},
 | ||
| 	reflect.SendDir: {types.SendOnly},
 | ||
| 	reflect.BothDir: {types.SendRecv},
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) {
 | ||
| 	// If we have access to the callsite,
 | ||
| 	// and the channel argument is a constant (as is usual),
 | ||
| 	// only generate the requested direction.
 | ||
| 	var dir reflect.ChanDir // unknown
 | ||
| 	if site := cgn.callersite; site != nil {
 | ||
| 		if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
 | ||
| 			v, _ := constant.Int64Val(c.Value)
 | ||
| 			if 0 <= v && v <= int64(reflect.BothDir) {
 | ||
| 				dir = reflect.ChanDir(v)
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	params := a.funcParams(cgn.obj)
 | ||
| 	a.addConstraint(&reflectChanOfConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		t:      params + 1,
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 		dirs:   dirMap[dir],
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func Indirect(v Value) Value ----------
 | ||
| 
 | ||
| // result = Indirect(v)
 | ||
| type reflectIndirectConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	v      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectIndirectConstraint) ptr() nodeid { return c.v }
 | ||
| func (c *reflectIndirectConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectIndirect.result")
 | ||
| }
 | ||
| func (c *reflectIndirectConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.v = mapping[c.v]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectIndirectConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectIndirectConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		vObj := nodeid(x)
 | ||
| 		tDyn, _, _ := a.taggedValue(vObj)
 | ||
| 		var res nodeid
 | ||
| 		if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok {
 | ||
| 			// load the payload of the pointer's tagged object
 | ||
| 			// into a new tagged object
 | ||
| 			res = a.makeTagged(tPtr.Elem(), c.cgn, nil)
 | ||
| 			a.load(res+1, vObj+1, 0, a.sizeof(tPtr.Elem()))
 | ||
| 		} else {
 | ||
| 			res = vObj
 | ||
| 		}
 | ||
| 
 | ||
| 		if a.addLabel(c.result, res) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Indirect(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectIndirectConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		v:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func MakeChan(Type) Value ----------
 | ||
| 
 | ||
| // result = MakeChan(typ)
 | ||
| type reflectMakeChanConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	typ    nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeChanConstraint) ptr() nodeid { return c.typ }
 | ||
| func (c *reflectMakeChanConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectMakeChan.result")
 | ||
| }
 | ||
| func (c *reflectMakeChanConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.typ = mapping[c.typ]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeChanConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeChanConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		typObj := nodeid(x)
 | ||
| 		T := a.rtypeTaggedValue(typObj)
 | ||
| 		tChan, ok := T.Underlying().(*types.Chan)
 | ||
| 		if !ok || tChan.Dir() != types.SendRecv {
 | ||
| 			continue // not a bidirectional channel type
 | ||
| 		}
 | ||
| 
 | ||
| 		obj := a.nextNode()
 | ||
| 		a.addNodes(tChan.Elem(), "reflect.MakeChan.value")
 | ||
| 		a.endObject(obj, c.cgn, nil)
 | ||
| 
 | ||
| 		// put its address in a new T-tagged object
 | ||
| 		id := a.makeTagged(T, c.cgn, nil)
 | ||
| 		a.addLabel(id+1, obj)
 | ||
| 
 | ||
| 		// flow the T-tagged object to the result
 | ||
| 		if a.addLabel(c.result, id) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰MakeChan(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectMakeChanConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		typ:    a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰MakeFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func MakeMap(Type) Value ----------
 | ||
| 
 | ||
| // result = MakeMap(typ)
 | ||
| type reflectMakeMapConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	typ    nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeMapConstraint) ptr() nodeid { return c.typ }
 | ||
| func (c *reflectMakeMapConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectMakeMap.result")
 | ||
| }
 | ||
| func (c *reflectMakeMapConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.typ = mapping[c.typ]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeMapConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeMapConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		typObj := nodeid(x)
 | ||
| 		T := a.rtypeTaggedValue(typObj)
 | ||
| 		tMap, ok := T.Underlying().(*types.Map)
 | ||
| 		if !ok {
 | ||
| 			continue // not a map type
 | ||
| 		}
 | ||
| 
 | ||
| 		mapObj := a.nextNode()
 | ||
| 		a.addNodes(tMap.Key(), "reflect.MakeMap.key")
 | ||
| 		a.addNodes(tMap.Elem(), "reflect.MakeMap.value")
 | ||
| 		a.endObject(mapObj, c.cgn, nil)
 | ||
| 
 | ||
| 		// put its address in a new T-tagged object
 | ||
| 		id := a.makeTagged(T, c.cgn, nil)
 | ||
| 		a.addLabel(id+1, mapObj)
 | ||
| 
 | ||
| 		// flow the T-tagged object to the result
 | ||
| 		if a.addLabel(c.result, id) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰MakeMap(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectMakeMapConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		typ:    a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func MakeSlice(Type) Value ----------
 | ||
| 
 | ||
| // result = MakeSlice(typ)
 | ||
| type reflectMakeSliceConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	typ    nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeSliceConstraint) ptr() nodeid { return c.typ }
 | ||
| func (c *reflectMakeSliceConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectMakeSlice.result")
 | ||
| }
 | ||
| func (c *reflectMakeSliceConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.typ = mapping[c.typ]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeSliceConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectMakeSliceConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		typObj := nodeid(x)
 | ||
| 		T := a.rtypeTaggedValue(typObj)
 | ||
| 		if _, ok := T.Underlying().(*types.Slice); !ok {
 | ||
| 			continue // not a slice type
 | ||
| 		}
 | ||
| 
 | ||
| 		obj := a.nextNode()
 | ||
| 		a.addNodes(sliceToArray(T), "reflect.MakeSlice")
 | ||
| 		a.endObject(obj, c.cgn, nil)
 | ||
| 
 | ||
| 		// put its address in a new T-tagged object
 | ||
| 		id := a.makeTagged(T, c.cgn, nil)
 | ||
| 		a.addLabel(id+1, obj)
 | ||
| 
 | ||
| 		// flow the T-tagged object to the result
 | ||
| 		if a.addLabel(c.result, id) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectMakeSliceConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		typ:    a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰MapOf(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func New(Type) Value ----------
 | ||
| 
 | ||
| // result = New(typ)
 | ||
| type reflectNewConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	typ    nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectNewConstraint) ptr() nodeid { return c.typ }
 | ||
| func (c *reflectNewConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectNew.result")
 | ||
| }
 | ||
| func (c *reflectNewConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.typ = mapping[c.typ]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectNewConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectNewConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		typObj := nodeid(x)
 | ||
| 		T := a.rtypeTaggedValue(typObj)
 | ||
| 
 | ||
| 		// allocate new T object
 | ||
| 		newObj := a.nextNode()
 | ||
| 		a.addNodes(T, "reflect.New")
 | ||
| 		a.endObject(newObj, c.cgn, nil)
 | ||
| 
 | ||
| 		// put its address in a new *T-tagged object
 | ||
| 		id := a.makeTagged(types.NewPointer(T), c.cgn, nil)
 | ||
| 		a.addLabel(id+1, newObj)
 | ||
| 
 | ||
| 		// flow the pointer to the result
 | ||
| 		if a.addLabel(c.result, id) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰New(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectNewConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		typ:    a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) {
 | ||
| 	ext۰reflect۰New(a, cgn)
 | ||
| 
 | ||
| 	// TODO(adonovan): also report dynamic calls to unsound intrinsics.
 | ||
| 	if site := cgn.callersite; site != nil {
 | ||
| 		a.warnf(site.pos(), "unsound: %s contains a reflect.NewAt() call", site.instr.Parent())
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // ---------- func PtrTo(Type) Type ----------
 | ||
| 
 | ||
| // result = PtrTo(t)
 | ||
| type reflectPtrToConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	t      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectPtrToConstraint) ptr() nodeid { return c.t }
 | ||
| func (c *reflectPtrToConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectPtrTo.result")
 | ||
| }
 | ||
| func (c *reflectPtrToConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.t = mapping[c.t]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectPtrToConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectPtrToConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		tObj := nodeid(x)
 | ||
| 		T := a.rtypeTaggedValue(tObj)
 | ||
| 
 | ||
| 		if typeTooHigh(T) {
 | ||
| 			continue
 | ||
| 		}
 | ||
| 
 | ||
| 		if a.addLabel(c.result, a.makeRtype(types.NewPointer(T))) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectPtrToConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Select(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func SliceOf(Type) Type ----------
 | ||
| 
 | ||
| // result = SliceOf(t)
 | ||
| type reflectSliceOfConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	t      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectSliceOfConstraint) ptr() nodeid { return c.t }
 | ||
| func (c *reflectSliceOfConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectSliceOf.result")
 | ||
| }
 | ||
| func (c *reflectSliceOfConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.t = mapping[c.t]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectSliceOfConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectSliceOfConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		tObj := nodeid(x)
 | ||
| 		T := a.rtypeTaggedValue(tObj)
 | ||
| 
 | ||
| 		if typeTooHigh(T) {
 | ||
| 			continue
 | ||
| 		}
 | ||
| 
 | ||
| 		if a.addLabel(c.result, a.makeRtype(types.NewSlice(T))) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectSliceOfConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func TypeOf(v Value) Type ----------
 | ||
| 
 | ||
| // result = TypeOf(i)
 | ||
| type reflectTypeOfConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	i      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectTypeOfConstraint) ptr() nodeid { return c.i }
 | ||
| func (c *reflectTypeOfConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectTypeOf.result")
 | ||
| }
 | ||
| func (c *reflectTypeOfConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.i = mapping[c.i]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectTypeOfConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectTypeOfConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		iObj := nodeid(x)
 | ||
| 		tDyn, _, _ := a.taggedValue(iObj)
 | ||
| 		if a.addLabel(c.result, a.makeRtype(tDyn)) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectTypeOfConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		i:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func ValueOf(interface{}) Value ----------
 | ||
| 
 | ||
| func ext۰reflect۰ValueOf(a *analysis, cgn *cgnode) {
 | ||
| 	// TODO(adonovan): when we start creating indirect tagged
 | ||
| 	// objects, we'll need to handle them specially here since
 | ||
| 	// they must never appear in the PTS of an interface{}.
 | ||
| 	a.copy(a.funcResults(cgn.obj), a.funcParams(cgn.obj), 1)
 | ||
| }
 | ||
| 
 | ||
| // ---------- func Zero(Type) Value ----------
 | ||
| 
 | ||
| // result = Zero(typ)
 | ||
| type reflectZeroConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	typ    nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectZeroConstraint) ptr() nodeid { return c.typ }
 | ||
| func (c *reflectZeroConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "reflectZero.result")
 | ||
| }
 | ||
| func (c *reflectZeroConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.typ = mapping[c.typ]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectZeroConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ)
 | ||
| }
 | ||
| 
 | ||
| func (c *reflectZeroConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		typObj := nodeid(x)
 | ||
| 		T := a.rtypeTaggedValue(typObj)
 | ||
| 
 | ||
| 		// TODO(adonovan): if T is an interface type, we need
 | ||
| 		// to create an indirect tagged object containing
 | ||
| 		// new(T).  To avoid updates of such shared values,
 | ||
| 		// we'll need another flag on indirect tagged objects
 | ||
| 		// that marks whether they are addressable or
 | ||
| 		// readonly, just like the reflect package does.
 | ||
| 
 | ||
| 		// memoize using a.reflectZeros[T]
 | ||
| 		var id nodeid
 | ||
| 		if z := a.reflectZeros.At(T); false && z != nil {
 | ||
| 			id = z.(nodeid)
 | ||
| 		} else {
 | ||
| 			id = a.makeTagged(T, c.cgn, nil)
 | ||
| 			a.reflectZeros.Set(T, id)
 | ||
| 		}
 | ||
| 		if a.addLabel(c.result, id) {
 | ||
| 			changed = true
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰Zero(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&reflectZeroConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		typ:    a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // -------------------- (*reflect.rtype) methods --------------------
 | ||
| 
 | ||
| // ---------- func (*rtype) Elem() Type ----------
 | ||
| 
 | ||
| // result = Elem(t)
 | ||
| type rtypeElemConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	t      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeElemConstraint) ptr() nodeid { return c.t }
 | ||
| func (c *rtypeElemConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rtypeElem.result")
 | ||
| }
 | ||
| func (c *rtypeElemConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.t = mapping[c.t]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeElemConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t)
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeElemConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	// Implemented by *types.{Map,Chan,Array,Slice,Pointer}.
 | ||
| 	type hasElem interface {
 | ||
| 		Elem() types.Type
 | ||
| 	}
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		tObj := nodeid(x)
 | ||
| 		T := a.nodes[tObj].obj.data.(types.Type)
 | ||
| 		if tHasElem, ok := T.Underlying().(hasElem); ok {
 | ||
| 			if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) {
 | ||
| 				changed = true
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰Elem(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rtypeElemConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (*rtype) Field(int) StructField ----------
 | ||
| // ---------- func (*rtype) FieldByName(string) (StructField, bool) ----------
 | ||
| 
 | ||
| // result = FieldByName(t, name)
 | ||
| // result = Field(t, _)
 | ||
| type rtypeFieldByNameConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	name   string // name of field; "" for unknown
 | ||
| 	t      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeFieldByNameConstraint) ptr() nodeid { return c.t }
 | ||
| func (c *rtypeFieldByNameConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result+3), "rtypeFieldByName.result.Type")
 | ||
| }
 | ||
| func (c *rtypeFieldByNameConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.t = mapping[c.t]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeFieldByNameConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = (*reflect.rtype).FieldByName(n%d, %q)", c.result, c.t, c.name)
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeFieldByNameConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	// type StructField struct {
 | ||
| 	// 0	__identity__
 | ||
| 	// 1	Name      string
 | ||
| 	// 2	PkgPath   string
 | ||
| 	// 3	Type      Type
 | ||
| 	// 4	Tag       StructTag
 | ||
| 	// 5	Offset    uintptr
 | ||
| 	// 6	Index     []int
 | ||
| 	// 7	Anonymous bool
 | ||
| 	// }
 | ||
| 
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		tObj := nodeid(x)
 | ||
| 		T := a.nodes[tObj].obj.data.(types.Type)
 | ||
| 		tStruct, ok := T.Underlying().(*types.Struct)
 | ||
| 		if !ok {
 | ||
| 			continue // not a struct type
 | ||
| 		}
 | ||
| 
 | ||
| 		n := tStruct.NumFields()
 | ||
| 		for i := 0; i < n; i++ {
 | ||
| 			f := tStruct.Field(i)
 | ||
| 			if c.name == "" || c.name == f.Name() {
 | ||
| 
 | ||
| 				// a.offsetOf(Type) is 3.
 | ||
| 				if id := c.result + 3; a.addLabel(id, a.makeRtype(f.Type())) {
 | ||
| 					a.addWork(id)
 | ||
| 				}
 | ||
| 				// TODO(adonovan): StructField.Index should be non-nil.
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode) {
 | ||
| 	// If we have access to the callsite,
 | ||
| 	// and the argument is a string constant,
 | ||
| 	// return only that field.
 | ||
| 	var name string
 | ||
| 	if site := cgn.callersite; site != nil {
 | ||
| 		if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
 | ||
| 			name = constant.StringVal(c.Value)
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	a.addConstraint(&rtypeFieldByNameConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		name:   name,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode) {
 | ||
| 	// No-one ever calls Field with a constant argument,
 | ||
| 	// so we don't specialize that case.
 | ||
| 	a.addConstraint(&rtypeFieldByNameConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode)    {} // TODO(adonovan)
 | ||
| func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
 | ||
| 
 | ||
| // ---------- func (*rtype) In/Out(i int) Type ----------
 | ||
| 
 | ||
| // result = In/Out(t, i)
 | ||
| type rtypeInOutConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	t      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| 	out    bool
 | ||
| 	i      int // -ve if not a constant
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeInOutConstraint) ptr() nodeid { return c.t }
 | ||
| func (c *rtypeInOutConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rtypeInOut.result")
 | ||
| }
 | ||
| func (c *rtypeInOutConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.t = mapping[c.t]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeInOutConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d, %d)", c.result, c.t, c.i)
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeInOutConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		tObj := nodeid(x)
 | ||
| 		T := a.nodes[tObj].obj.data.(types.Type)
 | ||
| 		sig, ok := T.Underlying().(*types.Signature)
 | ||
| 		if !ok {
 | ||
| 			continue // not a func type
 | ||
| 		}
 | ||
| 
 | ||
| 		tuple := sig.Params()
 | ||
| 		if c.out {
 | ||
| 			tuple = sig.Results()
 | ||
| 		}
 | ||
| 		for i, n := 0, tuple.Len(); i < n; i++ {
 | ||
| 			if c.i < 0 || c.i == i {
 | ||
| 				if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) {
 | ||
| 					changed = true
 | ||
| 				}
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰InOut(a *analysis, cgn *cgnode, out bool) {
 | ||
| 	// If we have access to the callsite,
 | ||
| 	// and the argument is an int constant,
 | ||
| 	// return only that parameter.
 | ||
| 	index := -1
 | ||
| 	if site := cgn.callersite; site != nil {
 | ||
| 		if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
 | ||
| 			v, _ := constant.Int64Val(c.Value)
 | ||
| 			index = int(v)
 | ||
| 		}
 | ||
| 	}
 | ||
| 	a.addConstraint(&rtypeInOutConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 		out:    out,
 | ||
| 		i:      index,
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰In(a *analysis, cgn *cgnode) {
 | ||
| 	ext۰reflect۰rtype۰InOut(a, cgn, false)
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰Out(a *analysis, cgn *cgnode) {
 | ||
| 	ext۰reflect۰rtype۰InOut(a, cgn, true)
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (*rtype) Key() Type ----------
 | ||
| 
 | ||
| // result = Key(t)
 | ||
| type rtypeKeyConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	t      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeKeyConstraint) ptr() nodeid { return c.t }
 | ||
| func (c *rtypeKeyConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result), "rtypeKey.result")
 | ||
| }
 | ||
| func (c *rtypeKeyConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.t = mapping[c.t]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeKeyConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t)
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeKeyConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	changed := false
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		tObj := nodeid(x)
 | ||
| 		T := a.nodes[tObj].obj.data.(types.Type)
 | ||
| 		if tMap, ok := T.Underlying().(*types.Map); ok {
 | ||
| 			if a.addLabel(c.result, a.makeRtype(tMap.Key())) {
 | ||
| 				changed = true
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if changed {
 | ||
| 		a.addWork(c.result)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰Key(a *analysis, cgn *cgnode) {
 | ||
| 	a.addConstraint(&rtypeKeyConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // ---------- func (*rtype) Method(int) (Method, bool) ----------
 | ||
| // ---------- func (*rtype) MethodByName(string) (Method, bool) ----------
 | ||
| 
 | ||
| // result = MethodByName(t, name)
 | ||
| // result = Method(t, _)
 | ||
| type rtypeMethodByNameConstraint struct {
 | ||
| 	cgn    *cgnode
 | ||
| 	name   string // name of method; "" for unknown
 | ||
| 	t      nodeid // (ptr)
 | ||
| 	result nodeid // (indirect)
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeMethodByNameConstraint) ptr() nodeid { return c.t }
 | ||
| func (c *rtypeMethodByNameConstraint) presolve(h *hvn) {
 | ||
| 	h.markIndirect(onodeid(c.result+3), "rtypeMethodByName.result.Type")
 | ||
| 	h.markIndirect(onodeid(c.result+4), "rtypeMethodByName.result.Func")
 | ||
| }
 | ||
| func (c *rtypeMethodByNameConstraint) renumber(mapping []nodeid) {
 | ||
| 	c.t = mapping[c.t]
 | ||
| 	c.result = mapping[c.result]
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeMethodByNameConstraint) String() string {
 | ||
| 	return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name)
 | ||
| }
 | ||
| 
 | ||
| // changeRecv returns sig with Recv prepended to Params().
 | ||
| func changeRecv(sig *types.Signature) *types.Signature {
 | ||
| 	params := sig.Params()
 | ||
| 	n := params.Len()
 | ||
| 	p2 := make([]*types.Var, n+1)
 | ||
| 	p2[0] = sig.Recv()
 | ||
| 	for i := 0; i < n; i++ {
 | ||
| 		p2[i+1] = params.At(i)
 | ||
| 	}
 | ||
| 	return types.NewSignature(nil, types.NewTuple(p2...), sig.Results(), sig.Variadic())
 | ||
| }
 | ||
| 
 | ||
| func (c *rtypeMethodByNameConstraint) solve(a *analysis, delta *nodeset) {
 | ||
| 	for _, x := range delta.AppendTo(a.deltaSpace) {
 | ||
| 		tObj := nodeid(x)
 | ||
| 		T := a.nodes[tObj].obj.data.(types.Type)
 | ||
| 
 | ||
| 		isIface := isInterface(T)
 | ||
| 
 | ||
| 		// We don't use Lookup(c.name) when c.name != "" to avoid
 | ||
| 		// ambiguity: >1 unexported methods could match.
 | ||
| 		mset := a.prog.MethodSets.MethodSet(T)
 | ||
| 		for i, n := 0, mset.Len(); i < n; i++ {
 | ||
| 			sel := mset.At(i)
 | ||
| 			if c.name == "" || c.name == sel.Obj().Name() {
 | ||
| 				// type Method struct {
 | ||
| 				// 0     __identity__
 | ||
| 				// 1	Name    string
 | ||
| 				// 2	PkgPath string
 | ||
| 				// 3	Type    Type
 | ||
| 				// 4	Func    Value
 | ||
| 				// 5	Index   int
 | ||
| 				// }
 | ||
| 
 | ||
| 				var sig *types.Signature
 | ||
| 				var fn *ssa.Function
 | ||
| 				if isIface {
 | ||
| 					sig = sel.Type().(*types.Signature)
 | ||
| 				} else {
 | ||
| 					fn = a.prog.MethodValue(sel)
 | ||
| 					// move receiver to params[0]
 | ||
| 					sig = changeRecv(fn.Signature)
 | ||
| 				}
 | ||
| 
 | ||
| 				// a.offsetOf(Type) is 3.
 | ||
| 				if id := c.result + 3; a.addLabel(id, a.makeRtype(sig)) {
 | ||
| 					a.addWork(id)
 | ||
| 				}
 | ||
| 				if fn != nil {
 | ||
| 					// a.offsetOf(Func) is 4.
 | ||
| 					if id := c.result + 4; a.addLabel(id, a.objectNode(nil, fn)) {
 | ||
| 						a.addWork(id)
 | ||
| 					}
 | ||
| 				}
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) {
 | ||
| 	// If we have access to the callsite,
 | ||
| 	// and the argument is a string constant,
 | ||
| 	// return only that method.
 | ||
| 	var name string
 | ||
| 	if site := cgn.callersite; site != nil {
 | ||
| 		if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
 | ||
| 			name = constant.StringVal(c.Value)
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	a.addConstraint(&rtypeMethodByNameConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		name:   name,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) {
 | ||
| 	// No-one ever calls Method with a constant argument,
 | ||
| 	// so we don't specialize that case.
 | ||
| 	a.addConstraint(&rtypeMethodByNameConstraint{
 | ||
| 		cgn:    cgn,
 | ||
| 		t:      a.funcParams(cgn.obj),
 | ||
| 		result: a.funcResults(cgn.obj),
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| // typeHeight returns the "height" of the type, which is roughly
 | ||
| // speaking the number of chan, map, pointer and slice type constructors
 | ||
| // at the root of T; these are the four type kinds that can be created
 | ||
| // via reflection.  Chan and map constructors are counted as double the
 | ||
| // height of slice and pointer constructors since they are less often
 | ||
| // deeply nested.
 | ||
| //
 | ||
| // The solver rules for type constructors must somehow bound the set of
 | ||
| // types they create to ensure termination of the algorithm in cases
 | ||
| // where the output of a type constructor flows to its input, e.g.
 | ||
| //
 | ||
| // 	func f(t reflect.Type) {
 | ||
| // 		f(reflect.PtrTo(t))
 | ||
| // 	}
 | ||
| //
 | ||
| // It does this by limiting the type height to k, but this still leaves
 | ||
| // a potentially exponential (4^k) number of of types that may be
 | ||
| // enumerated in pathological cases.
 | ||
| //
 | ||
| func typeHeight(T types.Type) int {
 | ||
| 	switch T := T.(type) {
 | ||
| 	case *types.Chan:
 | ||
| 		return 2 + typeHeight(T.Elem())
 | ||
| 	case *types.Map:
 | ||
| 		k := typeHeight(T.Key())
 | ||
| 		v := typeHeight(T.Elem())
 | ||
| 		if v > k {
 | ||
| 			k = v // max(k, v)
 | ||
| 		}
 | ||
| 		return 2 + k
 | ||
| 	case *types.Slice:
 | ||
| 		return 1 + typeHeight(T.Elem())
 | ||
| 	case *types.Pointer:
 | ||
| 		return 1 + typeHeight(T.Elem())
 | ||
| 	}
 | ||
| 	return 0
 | ||
| }
 | ||
| 
 | ||
| func typeTooHigh(T types.Type) bool {
 | ||
| 	return typeHeight(T) > 3
 | ||
| }
 |