143 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2016 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // This package constructs a simple control-flow graph (CFG) of the
 | |
| // statements and expressions within a single function.
 | |
| //
 | |
| // Use cfg.New to construct the CFG for a function body.
 | |
| //
 | |
| // The blocks of the CFG contain all the function's non-control
 | |
| // statements.  The CFG does not contain control statements such as If,
 | |
| // Switch, Select, and Branch, but does contain their subexpressions.
 | |
| // For example, this source code:
 | |
| //
 | |
| //	if x := f(); x != nil {
 | |
| //		T()
 | |
| //	} else {
 | |
| //		F()
 | |
| //	}
 | |
| //
 | |
| // produces this CFG:
 | |
| //
 | |
| //    1:  x := f()
 | |
| //        x != nil
 | |
| //        succs: 2, 3
 | |
| //    2:  T()
 | |
| //        succs: 4
 | |
| //    3:  F()
 | |
| //        succs: 4
 | |
| //    4:
 | |
| //
 | |
| // The CFG does contain Return statements; even implicit returns are
 | |
| // materialized (at the position of the function's closing brace).
 | |
| //
 | |
| // The CFG does not record conditions associated with conditional branch
 | |
| // edges, nor the short-circuit semantics of the && and || operators,
 | |
| // nor abnormal control flow caused by panic.  If you need this
 | |
| // information, use golang.org/x/tools/go/ssa instead.
 | |
| //
 | |
| package cfg
 | |
| 
 | |
| // Although the vet tool has type information, it is often extremely
 | |
| // fragmentary, so for simplicity this package does not depend on
 | |
| // go/types.  Consequently control-flow conditions are ignored even
 | |
| // when constant, and "mayReturn" information must be provided by the
 | |
| // client.
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/format"
 | |
| 	"go/token"
 | |
| )
 | |
| 
 | |
| // A CFG represents the control-flow graph of a single function.
 | |
| //
 | |
| // The entry point is Blocks[0]; there may be multiple return blocks.
 | |
| type CFG struct {
 | |
| 	Blocks []*Block // block[0] is entry; order otherwise undefined
 | |
| }
 | |
| 
 | |
| // A Block represents a basic block: a list of statements and
 | |
| // expressions that are always evaluated sequentially.
 | |
| //
 | |
| // A block may have 0-2 successors: zero for a return block or a block
 | |
| // that calls a function such as panic that never returns; one for a
 | |
| // normal (jump) block; and two for a conditional (if) block.
 | |
| type Block struct {
 | |
| 	Nodes []ast.Node // statements, expressions, and ValueSpecs
 | |
| 	Succs []*Block   // successor nodes in the graph
 | |
| 
 | |
| 	comment     string    // for debugging
 | |
| 	index       int32     // index within CFG.Blocks
 | |
| 	unreachable bool      // is block of stmts following return/panic/for{}
 | |
| 	succs2      [2]*Block // underlying array for Succs
 | |
| }
 | |
| 
 | |
| // New returns a new control-flow graph for the specified function body,
 | |
| // which must be non-nil.
 | |
| //
 | |
| // The CFG builder calls mayReturn to determine whether a given function
 | |
| // call may return.  For example, calls to panic, os.Exit, and log.Fatal
 | |
| // do not return, so the builder can remove infeasible graph edges
 | |
| // following such calls.  The builder calls mayReturn only for a
 | |
| // CallExpr beneath an ExprStmt.
 | |
| func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG {
 | |
| 	b := builder{
 | |
| 		mayReturn: mayReturn,
 | |
| 		cfg:       new(CFG),
 | |
| 	}
 | |
| 	b.current = b.newBlock("entry")
 | |
| 	b.stmt(body)
 | |
| 
 | |
| 	// Does control fall off the end of the function's body?
 | |
| 	// Make implicit return explicit.
 | |
| 	if b.current != nil && !b.current.unreachable {
 | |
| 		b.add(&ast.ReturnStmt{
 | |
| 			Return: body.End() - 1,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return b.cfg
 | |
| }
 | |
| 
 | |
| func (b *Block) String() string {
 | |
| 	return fmt.Sprintf("block %d (%s)", b.index, b.comment)
 | |
| }
 | |
| 
 | |
| // Return returns the return statement at the end of this block if present, nil otherwise.
 | |
| func (b *Block) Return() (ret *ast.ReturnStmt) {
 | |
| 	if len(b.Nodes) > 0 {
 | |
| 		ret, _ = b.Nodes[len(b.Nodes)-1].(*ast.ReturnStmt)
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Format formats the control-flow graph for ease of debugging.
 | |
| func (g *CFG) Format(fset *token.FileSet) string {
 | |
| 	var buf bytes.Buffer
 | |
| 	for _, b := range g.Blocks {
 | |
| 		fmt.Fprintf(&buf, ".%d: # %s\n", b.index, b.comment)
 | |
| 		for _, n := range b.Nodes {
 | |
| 			fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n))
 | |
| 		}
 | |
| 		if len(b.Succs) > 0 {
 | |
| 			fmt.Fprintf(&buf, "\tsuccs:")
 | |
| 			for _, succ := range b.Succs {
 | |
| 				fmt.Fprintf(&buf, " %d", succ.index)
 | |
| 			}
 | |
| 			buf.WriteByte('\n')
 | |
| 		}
 | |
| 		buf.WriteByte('\n')
 | |
| 	}
 | |
| 	return buf.String()
 | |
| }
 | |
| 
 | |
| func formatNode(fset *token.FileSet, n ast.Node) string {
 | |
| 	var buf bytes.Buffer
 | |
| 	format.Node(&buf, fset, n)
 | |
| 	// Indent secondary lines by a tab.
 | |
| 	return string(bytes.Replace(buf.Bytes(), []byte("\n"), []byte("\n\t"), -1))
 | |
| }
 | 
