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