dataflow

package
v0.0.0-...-a2136ea Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2023 License: BSD-3-Clause Imports: 14 Imported by: 1

Documentation

Overview

Package dataflow provides data flow analyses that can be performed on a previously constructed control flow graph, including a reaching definitions analysis and a live variables analysis for local variables.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefUse

func DefUse(cfg *cfg.CFG, info *packages.Package) map[ast.Stmt]map[ast.Stmt]struct{}

DefUse builds reaching definitions for a given control flow graph, returning a map that maps each statement that defines a variable (i.e., declares or assigns it) to the set of statements that use that variable.

Note: An assignment to a struct field or array element is treated as both a use and a definition of that variable, since only part of its value is assigned. For analysis purposes, it's treated as though the entire value is read, then part of it is modified, then the entire value is assigned back to the variable. (This is necessary for the analysis to produce correct results.)

No nodes from the cfg.Defers list will be returned in the output of this function as they are disjoint from a cfg's blocks. For analyzing the statements in the cfg.Defers list, each defer should be treated as though it has the same in and out sets as the cfg.Exit node.

func DefsReaching

func DefsReaching(stmt ast.Stmt, cfg *cfg.CFG, info *packages.Package) map[ast.Stmt]struct{}

DefsReaching builds reaching definitions for a given control flow graph, returning the set of statements that define a variable (i.e., declare or assign it) where that definition reaches the given statement.

Example
package main

import (
	"fmt"
	"go/ast"

	"github.com/godoctor/godoctor/analysis/cfg"
	"github.com/godoctor/godoctor/analysis/dataflow"
	"github.com/godoctor/godoctor/analysis/loader"
	"golang.org/x/tools/go/packages"
)

func main() {
	src := `
    package main

    import "fmt"

    func main() {
      a := 1
      b := 2
      c := 3
      a := b
      a, b := b, a
      c := a + b
    }
  `

	// can ignore overlay stuff, you can just load from disk normally
	var config packages.Config
	config.Dir = "testdata"
	config.Overlay = map[string][]byte{"main.go": []byte(src)}

	var laterError error

	prog, err := loader.Load(&config, func(err error) { laterError = err })
	if err != nil {
		return
	}
	if laterError != nil {
		return
	}

	var info *packages.Package
	for _, info = range prog.AllPackages {
		if info.Name == "main" {
			break
		}
	}

	funcOne := info.Syntax[0].Decls[1].(*ast.FuncDecl)
	c := cfg.FromFunc(funcOne)
	du := dataflow.DefUse(c, info)

	ast.Inspect(info.Syntax[0], func(n ast.Node) bool {
		switch stmt := n.(type) {
		case ast.Stmt:
			fmt.Println(len(du[stmt]))
			// do as you please
		}
		return true
	})
}
Output:

func LiveVars

func LiveVars(cfg *cfg.CFG, info *packages.Package) (in, out map[ast.Stmt]map[*types.Var]struct{})

LiveAt returns the in and out set of live variables for each block in a given control flow graph (cfg) in the context of a loader.Program, including the cfg.Entry and cfg.Exit nodes.

The traditional approach of holding the live variables at the exit node to the empty set has been deviated from in order to handle defers. The live variables in set of the cfg.Exit node will be set to the variables used in all cfg.Defers. No liveness is analyzed for the cfg.Defers themselves.

More formally:

IN[EXIT] = USE(each d in cfg.Defers)
OUT[EXIT] = {}
Example
package main

import (
	"go/ast"

	"github.com/godoctor/godoctor/analysis/cfg"
	"github.com/godoctor/godoctor/analysis/dataflow"
	"github.com/godoctor/godoctor/analysis/loader"
	"golang.org/x/tools/go/packages"
)

func main() {
	src := `
    package main

    import "fmt"

    func main() {
      a := 1
      b := 2
      c := 3
      a := b
      a, b := b, a
      c := a + b
    }
  `

	// can ignore overlay stuff, you can just load from disk normally
	var config packages.Config
	config.Dir = "testdata"
	config.Overlay = map[string][]byte{"main.go": []byte(src)}

	var laterError error

	prog, err := loader.Load(&config, func(err error) { laterError = err })
	if err != nil {
		return
	}
	if laterError != nil {
		return
	}

	var info *packages.Package
	for _, info = range prog.AllPackages {
		if info.Name == "main" {
			break
		}
	}

	funcOne := info.Syntax[0].Decls[1].(*ast.FuncDecl)
	cfg := cfg.FromFunc(funcOne)
	in, out := dataflow.LiveVars(cfg, info)

	ast.Inspect(info.Syntax[0], func(n ast.Node) bool {
		switch stmt := n.(type) {
		case ast.Stmt:
			_, _ = in[stmt], out[stmt]
			// do as you please
		}
		return true
	})
}
Output:

func PrintDefUseDot

func PrintDefUseDot(f io.Writer, fset *token.FileSet, info *packages.Package, cfg *cfg.CFG)

PrintDefUseDot prints a GraphViz DOT file showing the control flow graph with definition-use links superimposed.

This is used by the debug refactoring.

func PrintLiveVarsDot

func PrintLiveVarsDot(f io.Writer, fset *token.FileSet, info *packages.Package, cfg *cfg.CFG)

PrintLiveVarsDot prints a GraphViz DOT file showing the control flow graph with information about the liveness of local variables superimposed.

This is used by the debug refactoring.

func ReferencedVars

func ReferencedVars(stmts []ast.Stmt, info *packages.Package) (asgt, updt, decl, use map[*types.Var]struct{})

ReferencedVars returns the sets of local variables that are defined or used within the given list of statements (based on syntax).

func Vars

func Vars(node ast.Node, info *packages.Package) map[*types.Var]struct{}

Vars returns the set of variables appearing in node

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL