type node … type constant … func (c constant) Type() types.Type { … } func (c constant) String() string { … } type pointer … func (p pointer) Type() types.Type { … } func (p pointer) String() string { … } type mapKey … func (mk mapKey) Type() types.Type { … } func (mk mapKey) String() string { … } type mapValue … func (mv mapValue) Type() types.Type { … } func (mv mapValue) String() string { … } type sliceElem … func (s sliceElem) Type() types.Type { … } func (s sliceElem) String() string { … } type channelElem … func (c channelElem) Type() types.Type { … } func (c channelElem) String() string { … } type field … func (f field) Type() types.Type { … } func (f field) String() string { … } type global … func (g global) Type() types.Type { … } func (g global) String() string { … } type local … func (l local) Type() types.Type { … } func (l local) String() string { … } type indexedLocal … func (i indexedLocal) Type() types.Type { … } func (i indexedLocal) String() string { … } type function … func (f function) Type() types.Type { … } func (f function) String() string { … } type resultVar … func (o resultVar) Type() types.Type { … } func (o resultVar) String() string { … } type nestedPtrInterface … func (l nestedPtrInterface) Type() types.Type { … } func (l nestedPtrInterface) String() string { … } type nestedPtrFunction … func (p nestedPtrFunction) Type() types.Type { … } func (p nestedPtrFunction) String() string { … } type panicArg … func (p panicArg) Type() types.Type { … } func (p panicArg) String() string { … } type recoverReturn … func (r recoverReturn) Type() types.Type { … } func (r recoverReturn) String() string { … } type empty … type idx … type vtaGraph … func (g *vtaGraph) numNodes() int { … } func (g *vtaGraph) successors(x idx) func(yield func(y idx) bool) { … } // addEdge adds an edge x->y to the graph. func (g *vtaGraph) addEdge(x, y node) { … } // typePropGraph builds a VTA graph for a set of `funcs` and initial // `callgraph` needed to establish interprocedural edges. Returns the // graph and a map for unique type representatives. func typePropGraph(funcs map[*ssa.Function]bool, callees calleesFunc) (*vtaGraph, *typeutil.Map) { … } type builder … func (b *builder) visit(funcs map[*ssa.Function]bool) { … } func (b *builder) fun(f *ssa.Function) { … } func (b *builder) instr(instr ssa.Instruction) { … } func (b *builder) unop(u *ssa.UnOp) { … } func (b *builder) phi(p *ssa.Phi) { … } func (b *builder) tassert(a *ssa.TypeAssert) { … } // extract instruction t1 := t2[i] generates flows between t2[i] // and t1 where the source is indexed local representing a value // from tuple register t2 at index i and the target is t1. func (b *builder) extract(e *ssa.Extract) { … } func (b *builder) field(f *ssa.Field) { … } func (b *builder) fieldAddr(f *ssa.FieldAddr) { … } func (b *builder) send(s *ssa.Send) { … } // selekt generates flows for select statement // // a = select blocking/nonblocking [c_1 <- t_1, c_2 <- t_2, ..., <- o_1, <- o_2, ...] // // between receiving channel registers c_i and corresponding input register t_i. Further, // flows are generated between o_i and a[2 + i]. Note that a is a tuple register of type // <int, bool, r_1, r_2, ...> where the type of r_i is the element type of channel o_i. func (b *builder) selekt(s *ssa.Select) { … } // index instruction a := b[c] on slices creates flows between a and // SliceElem(t) flow where t is an interface type of c. Arrays and // slice elements are both modeled as SliceElem. func (b *builder) index(i *ssa.Index) { … } // indexAddr instruction a := &b[c] fetches address of a index // into the field so we create bidirectional flow a <-> SliceElem(t) // where t is an interface type of c. Arrays and slice elements are // both modeled as SliceElem. func (b *builder) indexAddr(i *ssa.IndexAddr) { … } // lookup handles map query commands a := m[b] where m is of type // map[...]V and V is an interface. It creates flows between `a` // and MapValue(V). func (b *builder) lookup(l *ssa.Lookup) { … } // mapUpdate handles map update commands m[b] = a where m is of type // map[K]V and K and V are interfaces. It creates flows between `a` // and MapValue(V) as well as between MapKey(K) and `b`. func (b *builder) mapUpdate(u *ssa.MapUpdate) { … } // next instruction <ok, key, value> := next r, where r // is a range over map or string generates flow between // key and MapKey as well value and MapValue nodes. func (b *builder) next(n *ssa.Next) { … } // addInFlowAliasEdges adds an edge r -> l to b.graph if l is a node that can // have an inflow, i.e., a node that represents an interface or an unresolved // function value. Similarly for the edge l -> r with an additional condition // of that l and r can potentially alias. func (b *builder) addInFlowAliasEdges(l, r node) { … } func (b *builder) closure(c *ssa.MakeClosure) { … } // panic creates a flow from arguments to panic instructions to return // registers of all recover statements in the program. Introduces a // global panic node Panic and // 1. for every panic statement p: add p -> Panic // 2. for every recover statement r: add Panic -> r (handled in call) // // TODO(zpavlinovic): improve precision by explicitly modeling how panic // values flow from callees to callers and into deferred recover instructions. func (b *builder) panic(p *ssa.Panic) { … } // call adds flows between arguments/parameters and return values/registers // for both static and dynamic calls, as well as go and defer calls. func (b *builder) call(c ssa.CallInstruction) { … } func addArgumentFlows(b *builder, c ssa.CallInstruction, f *ssa.Function) { … } // rtrn creates flow edges from the operands of the return // statement to the result variables of the enclosing function. func (b *builder) rtrn(r *ssa.Return) { … } func (b *builder) multiconvert(c *ssa.MultiConvert) { … } // addInFlowEdge adds s -> d to g if d is node that can have an inflow, i.e., a node // that represents an interface or an unresolved function value. Otherwise, there // is no interesting type flow so the edge is omitted. func (b *builder) addInFlowEdge(s, d node) { … } // Creates const, pointer, global, func, and local nodes based on register instructions. func (b *builder) nodeFromVal(val ssa.Value) node { … } // representative returns a unique representative for node `n`. Since // semantically equivalent types can have different implementations, // this method guarantees the same implementation is always used. func (b *builder) representative(n node) node { … } // canonicalize returns a type representative of `t` unique subject // to type map `canon`. func canonicalize(t types.Type, canon *typeutil.Map) types.Type { … }