github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/go/types/initorder.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package types 6 7 import ( 8 "container/heap" 9 "fmt" 10 ) 11 12 // initOrder computes the Info.InitOrder for package variables. 13 func (check *Checker) initOrder() { 14 // An InitOrder may already have been computed if a package is 15 // built from several calls to (*Checker).Files. Clear it. 16 check.Info.InitOrder = check.Info.InitOrder[:0] 17 18 // Compute the transposed object dependency graph and initialize 19 // a priority queue with the list of graph nodes. 20 pq := nodeQueue(dependencyGraph(check.objMap)) 21 heap.Init(&pq) 22 23 const debug = false 24 if debug { 25 fmt.Printf("Computing initialization order for %s\n\n", check.pkg) 26 fmt.Println("Object dependency graph:") 27 for obj, d := range check.objMap { 28 if len(d.deps) > 0 { 29 fmt.Printf("\t%s depends on\n", obj.Name()) 30 for dep := range d.deps { 31 fmt.Printf("\t\t%s\n", dep.Name()) 32 } 33 } else { 34 fmt.Printf("\t%s has no dependencies\n", obj.Name()) 35 } 36 } 37 fmt.Println() 38 39 fmt.Println("Transposed object dependency graph:") 40 for _, n := range pq { 41 fmt.Printf("\t%s depends on %d nodes\n", n.obj.Name(), n.in) 42 for _, out := range n.out { 43 fmt.Printf("\t\t%s is dependent\n", out.obj.Name()) 44 } 45 } 46 fmt.Println() 47 48 fmt.Println("Processing nodes:") 49 } 50 51 // Determine initialization order by removing the highest priority node 52 // (the one with the fewest dependencies) and its edges from the graph, 53 // repeatedly, until there are no nodes left. 54 // In a valid Go program, those nodes always have zero dependencies (after 55 // removing all incoming dependencies), otherwise there are initialization 56 // cycles. 57 mark := 0 58 emitted := make(map[*declInfo]bool) 59 for len(pq) > 0 { 60 // get the next node 61 n := heap.Pop(&pq).(*objNode) 62 63 if debug { 64 fmt.Printf("\t%s (src pos %d) depends on %d nodes now\n", 65 n.obj.Name(), n.obj.order(), n.in) 66 } 67 68 // if n still depends on other nodes, we have a cycle 69 if n.in > 0 { 70 mark++ // mark nodes using a different value each time 71 cycle := findPath(n, n, mark) 72 if i := valIndex(cycle); i >= 0 { 73 check.reportCycle(cycle, i) 74 } 75 // ok to continue, but the variable initialization order 76 // will be incorrect at this point since it assumes no 77 // cycle errors 78 } 79 80 // reduce dependency count of all dependent nodes 81 // and update priority queue 82 for _, out := range n.out { 83 out.in-- 84 heap.Fix(&pq, out.index) 85 } 86 87 // record the init order for variables with initializers only 88 v, _ := n.obj.(*Var) 89 info := check.objMap[v] 90 if v == nil || !info.hasInitializer() { 91 continue 92 } 93 94 // n:1 variable declarations such as: a, b = f() 95 // introduce a node for each lhs variable (here: a, b); 96 // but they all have the same initializer - emit only 97 // one, for the first variable seen 98 if emitted[info] { 99 continue // initializer already emitted, if any 100 } 101 emitted[info] = true 102 103 infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment) 104 if infoLhs == nil { 105 infoLhs = []*Var{v} 106 } 107 init := &Initializer{infoLhs, info.init} 108 check.Info.InitOrder = append(check.Info.InitOrder, init) 109 } 110 111 if debug { 112 fmt.Println() 113 fmt.Println("Initialization order:") 114 for _, init := range check.Info.InitOrder { 115 fmt.Printf("\t%s\n", init) 116 } 117 fmt.Println() 118 } 119 } 120 121 // findPath returns the (reversed) list of nodes z, ... c, b, a, 122 // such that there is a path (list of edges) from a to z. 123 // If there is no such path, the result is nil. 124 // Nodes marked with the value mark are considered "visited"; 125 // unvisited nodes are marked during the graph search. 126 func findPath(a, z *objNode, mark int) []*objNode { 127 if a.mark == mark { 128 return nil // node already seen 129 } 130 a.mark = mark 131 132 for _, n := range a.out { 133 if n == z { 134 return []*objNode{z} 135 } 136 if P := findPath(n, z, mark); P != nil { 137 return append(P, n) 138 } 139 } 140 141 return nil 142 } 143 144 // valIndex returns the index of the first constant or variable in a, 145 // if any; or a value < 0. 146 func valIndex(a []*objNode) int { 147 for i, n := range a { 148 switch n.obj.(type) { 149 case *Const, *Var: 150 return i 151 } 152 } 153 return -1 154 } 155 156 // reportCycle reports an error for the cycle starting at i. 157 func (check *Checker) reportCycle(cycle []*objNode, i int) { 158 obj := cycle[i].obj 159 check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name()) 160 // print cycle 161 for _ = range cycle { 162 check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented 163 i++ 164 if i >= len(cycle) { 165 i = 0 166 } 167 obj = cycle[i].obj 168 } 169 check.errorf(obj.Pos(), "\t%s", obj.Name()) 170 } 171 172 // An objNode represents a node in the object dependency graph. 173 // Each node b in a.out represents an edge a->b indicating that 174 // b depends on a. 175 // Nodes may be marked for cycle detection. A node n is marked 176 // if n.mark corresponds to the current mark value. 177 type objNode struct { 178 obj Object // object represented by this node 179 in int // number of nodes this node depends on 180 out []*objNode // list of nodes that depend on this node 181 index int // node index in list of nodes 182 mark int // for cycle detection 183 } 184 185 // dependencyGraph computes the transposed object dependency graph 186 // from the given objMap. The transposed graph is returned as a list 187 // of nodes; an edge d->n indicates that node n depends on node d. 188 func dependencyGraph(objMap map[Object]*declInfo) []*objNode { 189 // M maps each object to its corresponding node 190 M := make(map[Object]*objNode, len(objMap)) 191 for obj := range objMap { 192 M[obj] = &objNode{obj: obj} 193 } 194 195 // G is the graph of nodes n 196 G := make([]*objNode, len(M)) 197 i := 0 198 for obj, n := range M { 199 deps := objMap[obj].deps 200 n.in = len(deps) 201 for d := range deps { 202 d := M[d] // node n depends on node d 203 d.out = append(d.out, n) // add edge d->n 204 } 205 206 G[i] = n 207 n.index = i 208 i++ 209 } 210 211 return G 212 } 213 214 // nodeQueue implements the container/heap interface; 215 // a nodeQueue may be used as a priority queue. 216 type nodeQueue []*objNode 217 218 func (a nodeQueue) Len() int { return len(a) } 219 220 func (a nodeQueue) Swap(i, j int) { 221 x, y := a[i], a[j] 222 a[i], a[j] = y, x 223 x.index, y.index = j, i 224 } 225 226 func (a nodeQueue) Less(i, j int) bool { 227 x, y := a[i], a[j] 228 // nodes are prioritized by number of incoming dependencies (1st key) 229 // and source order (2nd key) 230 return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order() 231 } 232 233 func (a *nodeQueue) Push(x interface{}) { 234 panic("unreachable") 235 } 236 237 func (a *nodeQueue) Pop() interface{} { 238 n := len(*a) 239 x := (*a)[n-1] 240 x.index = -1 // for safety 241 *a = (*a)[:n-1] 242 return x 243 }