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  }