Chromium Code Reviews

Side by Side Diff: mojom/mojom_tool/utils/wellfounded_graphs.go

Issue 1915413002: Mojom frontend: Detect Ill-founded Types (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Fix comments as per code review. Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package utils 5 package utils
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "fmt" 9 "fmt"
10 ) 10 )
(...skipping 10 matching lines...)
21 // (ii) A circle node is well-founded iff all of its children are well-founded 21 // (ii) A circle node is well-founded iff all of its children are well-founded
22 // (iii) A non-leaf square node is well-founded iff at least one of its children is well-founded. 22 // (iii) A non-leaf square node is well-founded iff at least one of its children is well-founded.
23 // (See the paper for a more logically correct definition.) 23 // (See the paper for a more logically correct definition.)
24 // 24 //
25 // See the comments on the main function |CheckWellFounded| below for a descript ion 25 // See the comments on the main function |CheckWellFounded| below for a descript ion
26 // of the algorithm. 26 // of the algorithm.
27 27
28 /////////////////////////////////////////////////// 28 ///////////////////////////////////////////////////
29 // Node type 29 // Node type
30 // 30 //
31 // A |Node| is a node in a directed graph. 31 // A |Node| is a node in a directed graph. A user of this library is responsible
32 // for implementing this interface. This library will use the Go equality operat or
33 // to compare instances of |Node| and it is the responsibility of the user
34 // to ensure that x == y iff x and y represent the same logical node.
32 /////////////////////////////////////////////////// 35 ///////////////////////////////////////////////////
33 36
34 type Node interface { 37 type Node interface {
35 // Returns the list of children of this node. 38 // Returns the list of children of this node.
36 » OutEdges() []Node 39 » OutEdges() []OutEdge
37 40
38 // Returns whether or not this node is a square node. 41 // Returns whether or not this node is a square node.
39 IsSquare() bool 42 IsSquare() bool
40 43
41 // SetKnownWellFounded is invoked by the algorithm in order to set the 44 // SetKnownWellFounded is invoked by the algorithm in order to set the
42 // fact that the algorithm has determined that this node is well-founded . 45 // fact that the algorithm has determined that this node is well-founded .
43 // An implementation of |Node| should cache this--even between different 46 // An implementation of |Node| should cache this--even between different
44 // invocations of the algorithm on different starting nodes--and return 47 // invocations of the algorithm on different starting nodes--and return
45 // |true| from the method |KnownWellFounded| just in case this method ha s 48 // |true| from the method |KnownWellFounded| just in case this method ha s
46 // ever been invoked. In this way any nodes verified to be well-founded during 49 // ever been invoked. In this way any nodes verified to be well-founded during
47 // one run of the algorithm do not need to be checked during a later run of the 50 // one run of the algorithm do not need to be checked during a later run of the
48 // algorithm on a different starting node. 51 // algorithm on a different starting node.
49 SetKnownWellFounded() 52 SetKnownWellFounded()
50 53
51 // Returns whether or not the function |SetKnownWellFounded| has ever 54 // Returns whether or not the function |SetKnownWellFounded| has ever
52 // been invoked on this node, during the lifetime of a program using 55 // been invoked on this node, during the lifetime of a program using
53 // this library. 56 // this library.
54 KnownWellFounded() bool 57 KnownWellFounded() bool
55 58
56 » // Returns the name of this node, appropriate for debugging. 59 » // Returns a human-readable name for this node. Not used by the algorith m
57 » DebugName() string 60 » // but used for generating debugging and error messages.
61 » Name() string
62 }
63
64 type OutEdge struct {
65 » // The Label on an edge is opaque data that may be used by the client ho wever it wishes.
66 » // The algorithm never inspects this data.
67 » Label interface{}
68
69 » // The target node of the out edge.
70 » Target Node
58 } 71 }
59 72
60 /////////////////////////////////////////////////// 73 ///////////////////////////////////////////////////
61 // CheckWellFounded function 74 // CheckWellFounded function
62 // 75 //
63 // This is the main public function. 76 // This is the main public function.
64 /////////////////////////////////////////////////// 77 ///////////////////////////////////////////////////
65 78
66 // CheckWellFounded checks whether the sub-graph rooted at |root| contains any i ll-founded nodes. 79 // CheckWellFounded checks whether the sub-graph rooted at |root| contains any i ll-founded nodes.
67 // If any ill-founded nodes are detected then a non-nil |CycleDescription| is re turned containing 80 // If any ill-founded nodes are detected then a non-nil |CycleDescription| is re turned containing
(...skipping 59 matching lines...)
127 return finder.checkWellFounded(holder) 140 return finder.checkWellFounded(holder)
128 } 141 }
129 142
130 /////////////////////////////////////////////////// 143 ///////////////////////////////////////////////////
131 /// cycleFinder type 144 /// cycleFinder type
132 /// 145 ///
133 /// A cycleFinder is an object that holds state during the execution of the algo rithm. 146 /// A cycleFinder is an object that holds state during the execution of the algo rithm.
134 /////////////////////////////////////////////////// 147 ///////////////////////////////////////////////////
135 type cycleFinder struct { 148 type cycleFinder struct {
136 149
137 » // Maps each node seen by the computation to its holder. 150 » // Maps each node seen by the computation to its holder. Note that this assumes
151 » // that the implementation of Node uses values that are comparable and
152 » // obey identity semantics.
138 nodeToHolder map[Node]*nodeHolder 153 nodeToHolder map[Node]*nodeHolder
139 154
140 foundationSet, pendingSet NodeSet 155 foundationSet, pendingSet NodeSet
141 156
142 visitationIndex int 157 visitationIndex int
143 currentPath nodeStack
144 158
145 debugDataRequest *debugData 159 debugDataRequest *debugData
146 } 160 }
147 161
148 type debugData struct { 162 type debugData struct {
149 initialPendingSet []Node 163 initialPendingSet []Node
150 initialFoundationSet []Node 164 initialFoundationSet []Node
151 } 165 }
152 166
153 func makeCycleFinder() cycleFinder { 167 func makeCycleFinder() cycleFinder {
154 finder := cycleFinder{} 168 finder := cycleFinder{}
155 finder.nodeToHolder = make(map[Node]*nodeHolder) 169 finder.nodeToHolder = make(map[Node]*nodeHolder)
156 finder.foundationSet = MakeNodeSet() 170 finder.foundationSet = MakeNodeSet()
157 finder.pendingSet = MakeNodeSet() 171 finder.pendingSet = MakeNodeSet()
158 finder.currentPath = nodeStack{}
159 return finder 172 return finder
160 } 173 }
161 174
162 // checkWellFounded contains the top-level implementation of the algorithm. 175 // checkWellFounded contains the top-level implementation of the algorithm.
163 func (finder *cycleFinder) checkWellFounded(nodeHolder *nodeHolder) *CycleDescri ption { 176 func (finder *cycleFinder) checkWellFounded(nodeHolder *nodeHolder) *CycleDescri ption {
164 if nodeHolder.node.KnownWellFounded() { 177 if nodeHolder.node.KnownWellFounded() {
165 // This node is already known to be well-founded because of an e arlier 178 // This node is already known to be well-founded because of an e arlier
166 // execution of the algorithm. 179 // execution of the algorithm.
167 return nil 180 return nil
168 } 181 }
(...skipping 30 matching lines...)
199 if minIllfoundedNode == nil || n.visitationOrder < minVisitation Order { 212 if minIllfoundedNode == nil || n.visitationOrder < minVisitation Order {
200 minVisitationOrder = n.visitationOrder 213 minVisitationOrder = n.visitationOrder
201 minIllfoundedNode = n.node 214 minIllfoundedNode = n.node
202 } 215 }
203 } 216 }
204 217
205 // Starting from the ill-founded node with the least visitation order, 218 // Starting from the ill-founded node with the least visitation order,
206 // build a canonical cycle. 219 // build a canonical cycle.
207 freshCycleFinder := makeCycleFinder() 220 freshCycleFinder := makeCycleFinder()
208 holder := freshCycleFinder.holderForNode(minIllfoundedNode) 221 holder := freshCycleFinder.holderForNode(minIllfoundedNode)
209 » return freshCycleFinder.findKnownIllFoundedCycle(holder) 222 » return freshCycleFinder.findKnownIllFoundedCycle(holder, nil)
210 } 223 }
211 224
212 // checkWellFoundedPhase1 is a recursive helper function that does a depth-first traversal 225 // checkWellFoundedPhase1 is a recursive helper function that does a depth-first traversal
213 // of the graph rooted at |nodeHolder|. The goal of phase 1 is to mark as many 226 // of the graph rooted at |nodeHolder|. The goal of phase 1 is to mark as many
214 // of the nodes as possible as |KnownWellFounded| and to set up the |pendingSet| , 227 // of the nodes as possible as |KnownWellFounded| and to set up the |pendingSet| ,
215 // the |foundationSet|, and the |parentsToBeNotified| sets so that the remaining 228 // the |foundationSet|, and the |parentsToBeNotified| sets so that the remaining
216 // well-founded nodes will be marked as |KnownWellFounded| in phase 2. 229 // well-founded nodes will be marked as |KnownWellFounded| in phase 2.
217 // 230 //
218 // In more detail the following steps are performed for each node x: 231 // In more detail the following steps are performed for each node x:
219 // (a1) If it can be verified during the traversal that x is well-founded then 232 // (a1) If it can be verified during the traversal that x is well-founded then
(...skipping 19 matching lines...)
239 // of the algorithm and so we treat this node as a leaf node. 252 // of the algorithm and so we treat this node as a leaf node.
240 nodeHolder.state = vsVisitEnded 253 nodeHolder.state = vsVisitEnded
241 return 254 return
242 } 255 }
243 256
244 // Mark the visit as started. 257 // Mark the visit as started.
245 nodeHolder.state = vsVisitStarted 258 nodeHolder.state = vsVisitStarted
246 259
247 // Next we examine each of the children and recurse into the unvisited o nes. 260 // Next we examine each of the children and recurse into the unvisited o nes.
248 sawUnverifiedChild := false 261 sawUnverifiedChild := false
249 » for _, child := range nodeHolder.node.OutEdges() { 262 » for _, edge := range nodeHolder.node.OutEdges() {
250 » » childHolder := finder.holderForNode(child) 263 » » childHolder := finder.holderForNode(edge.Target)
251 if childHolder.state == vsUnvisited { 264 if childHolder.state == vsUnvisited {
252 // Recursively visit this child. 265 // Recursively visit this child.
253 finder.checkWellFoundedPhase1(childHolder) 266 finder.checkWellFoundedPhase1(childHolder)
254 } 267 }
255 268
256 // After having visited a child we use the results to update the status of this node. 269 // After having visited a child we use the results to update the status of this node.
257 // We could express the logic here more concisely, but the logi c is easier 270 // We could express the logic here more concisely, but the logi c is easier
258 // to understand if we treat circles and squares seperately, 271 // to understand if we treat circles and squares seperately,
259 if nodeHolder.node.IsSquare() { 272 if nodeHolder.node.IsSquare() {
260 if nodeHolder.node.KnownWellFounded() { 273 if nodeHolder.node.KnownWellFounded() {
(...skipping 55 matching lines...)
316 // to be well-founded during phase 1, pruned to nodes that have parents that 329 // to be well-founded during phase 1, pruned to nodes that have parents that
317 // are in the |pendingSet|.) We iteratively remove a node n from the foundation set and 330 // are in the |pendingSet|.) We iteratively remove a node n from the foundation set and
318 // for each of its parents p for which p is in the pending set and for which we can now verify 331 // for each of its parents p for which p is in the pending set and for which we can now verify
319 // well-foundedness, we remove p from the pending set and add it to the foundati on set. 332 // well-foundedness, we remove p from the pending set and add it to the foundati on set.
320 func (finder *cycleFinder) checkWellFoundedPhase2() { 333 func (finder *cycleFinder) checkWellFoundedPhase2() {
321 for n := finder.foundationSet.removeRandomElement(); n != nil; n = finde r.foundationSet.removeRandomElement() { 334 for n := finder.foundationSet.removeRandomElement(); n != nil; n = finde r.foundationSet.removeRandomElement() {
322 for p, _ := range n.parentsToBeNotified.elements { 335 for p, _ := range n.parentsToBeNotified.elements {
323 if finder.pendingSet.Contains(p) { 336 if finder.pendingSet.Contains(p) {
324 knownWellFounded := true 337 knownWellFounded := true
325 if !p.node.IsSquare() { 338 if !p.node.IsSquare() {
326 » » » » » for _, child := range p.node.OutEdges() { 339 » » » » » for _, edge := range p.node.OutEdges() {
340 » » » » » » child := edge.Target
327 if child != p.node && !child.Kno wnWellFounded() { 341 if child != p.node && !child.Kno wnWellFounded() {
328 knownWellFounded = false 342 knownWellFounded = false
329 break 343 break
330 } 344 }
331 } 345 }
332 } 346 }
333 if knownWellFounded { 347 if knownWellFounded {
334 p.node.SetKnownWellFounded() 348 p.node.SetKnownWellFounded()
335 finder.foundationSet.Add(p) 349 finder.foundationSet.Add(p)
336 finder.pendingSet.Remove(p) 350 finder.pendingSet.Remove(p)
337 } 351 }
338 } 352 }
339 } 353 }
340 } 354 }
341 } 355 }
342 356
357 // A pathElement is a node and one of its out-edges.
358 type pathElement struct {
359 node Node
360 edge OutEdge
361 }
362
363 type nodePath []pathElement
364
343 // findKnownIllFoundedCycle finds and returns a |CycleDescription| starting from a node that is known 365 // findKnownIllFoundedCycle finds and returns a |CycleDescription| starting from a node that is known
344 // to be ill-founded. This proceeds by following edges from an ill-founded node to 366 // to be ill-founded. This proceeds by following edges from an ill-founded node to
345 // an ill-founded child node until a cycle is formed. We return a *canonical* cy cle, 367 // an ill-founded child node until a cycle is formed. We return a *canonical* cy cle,
346 // meaning we start from the node with the least possible visit index and follow edges to 368 // meaning we start from the node with the least possible visit index and follow edges to
347 // the child node with the least possible visit index. This is done in order to make testing of the algorithm easier. 369 // the child node with the least possible visit index. This is done in order to make testing of the algorithm easier.
348 // We are not concerned with optimizing the performance of phase 3 because in th e intended application 370 // We are not concerned with optimizing the performance of phase 3 because in th e intended application
349 // phase 3 can occur at most once in the lifetime of a program: Once an ill-foun ded node is detected the 371 // phase 3 can occur at most once in the lifetime of a program: Once an ill-foun ded node is detected the
350 // program exits with a cycle description allowing the user to fix the ill-found edness. 372 // program exits with a cycle description allowing the user to fix the ill-found edness.
351 func (finder *cycleFinder) findKnownIllFoundedCycle(nodeHolder *nodeHolder) *Cyc leDescription { 373 //
374 // This method is recursive. To initiate the recursion |nodeHolder| should be se t to the starting node and
375 // |path| should be nil. In subsequent recursive calls |nodeHolder| is the curre nt node of the walk and
376 // |path| is the path from the starting node to the current node.
377 func (finder *cycleFinder) findKnownIllFoundedCycle(nodeHolder *nodeHolder, path nodePath) *CycleDescription {
378 » // If this is the start of the recursion initialize the path.
379 » if path == nil {
380 » » path = make(nodePath, 0)
381 » }
382
352 // Mark the current node as started 383 // Mark the current node as started
353 nodeHolder.state = vsVisitStarted 384 nodeHolder.state = vsVisitStarted
354 » finder.currentPath.Push(nodeHolder) 385 » for _, edge := range nodeHolder.node.OutEdges() {
355 » for _, child := range nodeHolder.node.OutEdges() { 386 » » childHolder := finder.holderForNode(edge.Target)
356 » » childHolder := finder.holderForNode(child)
357 if childHolder.state == vsVisitStarted { 387 if childHolder.state == vsVisitStarted {
358 // If the child has been started but not finished then w e have found a cycle 388 // If the child has been started but not finished then w e have found a cycle
359 // from the child to the current node back to the child. 389 // from the child to the current node back to the child.
360 » » » return newCycleDescription(finder.currentPath.elements, childHolder, nodeHolder) 390 » » » path = append(path, pathElement{nodeHolder.node, edge})
391 » » » return newCycleDescription(path, childHolder.node, nodeH older.node)
361 } else if !childHolder.node.KnownWellFounded() { 392 } else if !childHolder.node.KnownWellFounded() {
362 » » » return finder.findKnownIllFoundedCycle(childHolder) 393 » » » path = append(path, pathElement{nodeHolder.node, edge})
394 » » » return finder.findKnownIllFoundedCycle(childHolder, path )
363 } 395 }
364 } 396 }
365 panic("Program logic error: Could not find a known ill-founded cycle.") 397 panic("Program logic error: Could not find a known ill-founded cycle.")
366 } 398 }
367 399
368 // Returns the nodeHolder for the given node. 400 // Returns the nodeHolder for the given node.
369 func (finder *cycleFinder) holderForNode(node Node) *nodeHolder { 401 func (finder *cycleFinder) holderForNode(node Node) *nodeHolder {
370 if holder, found := finder.nodeToHolder[node]; found { 402 if holder, found := finder.nodeToHolder[node]; found {
371 return holder 403 return holder
372 } 404 }
(...skipping 33 matching lines...)
406 438
407 func newNodeHolder(node Node, visitationOrder int) *nodeHolder { 439 func newNodeHolder(node Node, visitationOrder int) *nodeHolder {
408 nodeHolder := new(nodeHolder) 440 nodeHolder := new(nodeHolder)
409 nodeHolder.node = node 441 nodeHolder.node = node
410 nodeHolder.parentsToBeNotified = MakeNodeSet() 442 nodeHolder.parentsToBeNotified = MakeNodeSet()
411 nodeHolder.state = vsUnvisited 443 nodeHolder.state = vsUnvisited
412 nodeHolder.visitationOrder = visitationOrder 444 nodeHolder.visitationOrder = visitationOrder
413 return nodeHolder 445 return nodeHolder
414 } 446 }
415 447
416 //////////////////////////////////////////////
417 /// nodeStack type
418 //////////////////////////////////////////////
419
420 // A nodeStack is a stack of *nodeHolders
421 type nodeStack struct {
422 elements []*nodeHolder
423 }
424
425 func (stack *nodeStack) Push(n *nodeHolder) {
426 stack.elements = append(stack.elements, n)
427 }
428
429 func (stack *nodeStack) Size() int {
430 return len(stack.elements)
431 }
432
433 func (stack *nodeStack) Pop() (n *nodeHolder) {
434 lastIndex := stack.Size() - 1
435 n = stack.elements[lastIndex]
436 stack.elements = stack.elements[:lastIndex]
437 return
438 }
439
440 func (stack *nodeStack) Peek() (n *nodeHolder) {
441 return stack.elements[stack.Size()-1]
442 }
443
444 func (stack *nodeStack) String() string {
445 var buffer bytes.Buffer
446 fmt.Fprintf(&buffer, "[")
447 first := true
448 for _, e := range stack.elements {
449 if !first {
450 fmt.Fprintf(&buffer, ", ")
451 }
452 fmt.Fprintf(&buffer, "%s", e.node.DebugName())
453 first = false
454 }
455 fmt.Fprintln(&buffer, "]")
456 return buffer.String()
457 }
458
459 /////////////////////////////////////////////////// 448 ///////////////////////////////////////////////////
460 /// NodeSet type 449 /// NodeSet type
461 /////////////////////////////////////////////////// 450 ///////////////////////////////////////////////////
462 451
463 // A NodeSet is a set of nodeHolders. 452 // A NodeSet is a set of nodeHolders.
464 type NodeSet struct { 453 type NodeSet struct {
465 elements map[*nodeHolder]bool 454 elements map[*nodeHolder]bool
466 } 455 }
467 456
468 // MakeNodeSet makes a new empty NodeSet. 457 // MakeNodeSet makes a new empty NodeSet.
(...skipping 66 matching lines...)
535 524
536 func (set *NodeSet) Size() int { 525 func (set *NodeSet) Size() int {
537 return len(set.elements) 526 return len(set.elements)
538 } 527 }
539 528
540 // compareNodeSets is a package-private method used in our tests. It returns 529 // compareNodeSets is a package-private method used in our tests. It returns
541 // a non-nil error in case expected is not equal to actual. 530 // a non-nil error in case expected is not equal to actual.
542 func compareNodeSets(expected, actual *NodeSet) error { 531 func compareNodeSets(expected, actual *NodeSet) error {
543 for n, _ := range expected.elements { 532 for n, _ := range expected.elements {
544 if !actual.Contains(n) { 533 if !actual.Contains(n) {
545 » » » return fmt.Errorf("%s is in expected but not actual", n. node.DebugName()) 534 » » » return fmt.Errorf("%s is in expected but not actual", n. node.Name())
546 } 535 }
547 } 536 }
548 for n, _ := range actual.elements { 537 for n, _ := range actual.elements {
549 if !expected.Contains(n) { 538 if !expected.Contains(n) {
550 » » » return fmt.Errorf("%s is in actual but not expected", n. node.DebugName()) 539 » » » return fmt.Errorf("%s is in actual but not expected", n. node.Name())
551 } 540 }
552 } 541 }
553 return nil 542 return nil
554 } 543 }
555 544
556 // String returns a human readable string representation of |set|. 545 // String returns a human readable string representation of |set|.
557 func (set *NodeSet) String() string { 546 func (set *NodeSet) String() string {
558 var buffer bytes.Buffer 547 var buffer bytes.Buffer
559 fmt.Fprintf(&buffer, "{") 548 fmt.Fprintf(&buffer, "{")
560 first := true 549 first := true
561 for e, _ := range set.elements { 550 for e, _ := range set.elements {
562 if !first { 551 if !first {
563 fmt.Fprintf(&buffer, ", ") 552 fmt.Fprintf(&buffer, ", ")
564 } 553 }
565 » » fmt.Fprintf(&buffer, "%s", e.node.DebugName()) 554 » » fmt.Fprintf(&buffer, "%s", e.node.Name())
566 first = false 555 first = false
567 } 556 }
568 fmt.Fprintln(&buffer, "}") 557 fmt.Fprintln(&buffer, "}")
569 return buffer.String() 558 return buffer.String()
570 } 559 }
571 560
572 /////////////////////////////////////////////////// 561 ///////////////////////////////////////////////////
573 // CycleDescription type 562 // CycleDescription type
574 // 563 //
575 // A |CycleDescription| describes a cycle in a directed graph. 564 // A |CycleDescription| describes a cycle in a directed graph.
576 /////////////////////////////////////////////////// 565 ///////////////////////////////////////////////////
577 566
578 type CycleDescription struct { 567 type CycleDescription struct {
579 » first, last Node 568 » First, Last Node
580 » path []Node 569 » Path []OutEdge
581 } 570 }
582 571
583 func (c *CycleDescription) String() string { 572 func (c *CycleDescription) String() string {
584 var buffer bytes.Buffer 573 var buffer bytes.Buffer
585 » fmt.Fprintf(&buffer, "first:%s", c.first.DebugName()) 574 » fmt.Fprintf(&buffer, "first:%s", c.First.Name())
586 » fmt.Fprintf(&buffer, ", last:%s", c.last.DebugName()) 575 » fmt.Fprintf(&buffer, ", last:%s", c.Last.Name())
587 fmt.Fprintf(&buffer, ", path:{") 576 fmt.Fprintf(&buffer, ", path:{")
588 » first := true 577 » fmt.Fprintf(&buffer, "%s", c.First.Name())
589 » for _, n := range c.path { 578 » for _, edge := range c.Path {
590 » » if !first { 579 » » if edge.Target != c.First {
591 fmt.Fprintf(&buffer, ", ") 580 fmt.Fprintf(&buffer, ", ")
581 fmt.Fprintf(&buffer, "%s", edge.Target.Name())
592 } 582 }
593 fmt.Fprintf(&buffer, "%s", n.DebugName())
594 first = false
595 } 583 }
596 fmt.Fprintln(&buffer, "}") 584 fmt.Fprintln(&buffer, "}")
597 return buffer.String() 585 return buffer.String()
598 } 586 }
599 587
600 func newCycleDescription(path []*nodeHolder, first, last *nodeHolder) *CycleDesc ription { 588 // newCycleDescription builds a CycleDescription based on the given data.
589 // |path| should be a nodePath that ends with a cycle. That is it should look li ke
590 // {x1, ->x2}, {x2, ->x3}, {x3, ->x4}, {x4, ->x5}, {x5, ->x2}
591 //
592 // |cycleStart| should be the first node of |path| included in the cycle. In
593 // the above example it would be x2.
594 //
595 // |end| should be the last node of the path. In the above example it would be x 5.
596 //
597 // The return value using our example would be:
598 // {First: x2, Last: x5, Path:[{x2, x2->x3}, {x3, x3->x4}, {x4, x4->x5}, {x5, x5 ->x2}]}
599 func newCycleDescription(path nodePath, cycleStart, end Node) *CycleDescription {
600 » lastNode := path[len(path)-1].node
601 » if lastNode != end {
602 » » panic(fmt.Sprintf("%s != %s", lastNode.Name(), end.Name()))
603 » }
604 » lastTarget := path[len(path)-1].edge.Target
605 » if lastTarget != cycleStart {
606 » » panic(fmt.Sprintf("(%T)%s != (%T)%s", lastTarget, lastTarget.Nam e(), cycleStart, cycleStart.Name()))
607 » }
601 description := CycleDescription{} 608 description := CycleDescription{}
602 » description.first = first.node 609 » description.First = cycleStart
603 » description.last = last.node 610 » description.Last = end
604 » description.path = make([]Node, 0, len(path)) 611 » description.Path = make([]OutEdge, 0, len(path))
605 » for _, n := range path { 612 » for _, element := range path {
606 » » if len(description.path) > 0 || n.node == first.node { 613 » » if len(description.Path) > 0 || element.node == cycleStart {
607 » » » description.path = append(description.path, n.node) 614 » » » description.Path = append(description.Path, element.edge )
608 } 615 }
609 } 616 }
610 » if description.path[len(description.path)-1] != last.node { 617 » if description.Path[len(description.Path)-1].Target != cycleStart {
611 » » panic(fmt.Sprintf("%s != %s", description.path[len(description.p ath)-1], last.node)) 618 » » panic(fmt.Sprintf("%s != %s", description.Path[len(description.P ath)-1].Target.Name(), cycleStart.Name()))
612 } 619 }
613 return &description 620 return &description
614 } 621 }
OLDNEW
« no previous file with comments | « mojom/mojom_tool/serialization/serialization_test.go ('k') | mojom/mojom_tool/utils/wellfounded_graphs_test.go » ('j') | no next file with comments »

Powered by Google App Engine