You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gosuv/vendor/github.com/gopherjs/gopherjs/compiler/analysis/info.go

255 lines
6.3 KiB

package analysis
import (
"go/ast"
"go/token"
"go/types"
"github.com/gopherjs/gopherjs/compiler/astutil"
"github.com/gopherjs/gopherjs/compiler/typesutil"
)
type continueStmt struct {
forStmt *ast.ForStmt
analyzeStack []ast.Node
}
type Info struct {
*types.Info
Pkg *types.Package
IsBlocking func(*types.Func) bool
HasPointer map[*types.Var]bool
FuncDeclInfos map[*types.Func]*FuncInfo
FuncLitInfos map[*ast.FuncLit]*FuncInfo
InitFuncInfo *FuncInfo
allInfos []*FuncInfo
comments ast.CommentMap
}
type FuncInfo struct {
HasDefer bool
Flattened map[ast.Node]bool
Blocking map[ast.Node]bool
GotoLabel map[*types.Label]bool
LocalCalls map[*types.Func][][]ast.Node
ContinueStmts []continueStmt
p *Info
analyzeStack []ast.Node
}
func (info *Info) newFuncInfo() *FuncInfo {
funcInfo := &FuncInfo{
p: info,
Flattened: make(map[ast.Node]bool),
Blocking: make(map[ast.Node]bool),
GotoLabel: make(map[*types.Label]bool),
LocalCalls: make(map[*types.Func][][]ast.Node),
}
info.allInfos = append(info.allInfos, funcInfo)
return funcInfo
}
func AnalyzePkg(files []*ast.File, fileSet *token.FileSet, typesInfo *types.Info, typesPkg *types.Package, isBlocking func(*types.Func) bool) *Info {
info := &Info{
Info: typesInfo,
Pkg: typesPkg,
HasPointer: make(map[*types.Var]bool),
comments: make(ast.CommentMap),
IsBlocking: isBlocking,
FuncDeclInfos: make(map[*types.Func]*FuncInfo),
FuncLitInfos: make(map[*ast.FuncLit]*FuncInfo),
}
info.InitFuncInfo = info.newFuncInfo()
for _, file := range files {
for k, v := range ast.NewCommentMap(fileSet, file, file.Comments) {
info.comments[k] = v
}
ast.Walk(info.InitFuncInfo, file)
}
for {
done := true
for _, funcInfo := range info.allInfos {
for obj, calls := range funcInfo.LocalCalls {
if len(info.FuncDeclInfos[obj].Blocking) != 0 {
for _, call := range calls {
funcInfo.markBlocking(call)
}
delete(funcInfo.LocalCalls, obj)
done = false
}
}
}
if done {
break
}
}
for _, funcInfo := range info.allInfos {
for _, continueStmt := range funcInfo.ContinueStmts {
if funcInfo.Blocking[continueStmt.forStmt.Post] {
funcInfo.markBlocking(continueStmt.analyzeStack)
}
}
}
return info
}
func (c *FuncInfo) Visit(node ast.Node) ast.Visitor {
if node == nil {
if len(c.analyzeStack) != 0 {
c.analyzeStack = c.analyzeStack[:len(c.analyzeStack)-1]
}
return nil
}
c.analyzeStack = append(c.analyzeStack, node)
switch n := node.(type) {
case *ast.FuncDecl:
newInfo := c.p.newFuncInfo()
c.p.FuncDeclInfos[c.p.Defs[n.Name].(*types.Func)] = newInfo
return newInfo
case *ast.FuncLit:
newInfo := c.p.newFuncInfo()
c.p.FuncLitInfos[n] = newInfo
return newInfo
case *ast.BranchStmt:
switch n.Tok {
case token.GOTO:
for _, n2 := range c.analyzeStack {
c.Flattened[n2] = true
}
c.GotoLabel[c.p.Uses[n.Label].(*types.Label)] = true
case token.CONTINUE:
if n.Label != nil {
label := c.p.Uses[n.Label].(*types.Label)
for i := len(c.analyzeStack) - 1; i >= 0; i-- {
if labelStmt, ok := c.analyzeStack[i].(*ast.LabeledStmt); ok && c.p.Defs[labelStmt.Label] == label {
if _, ok := labelStmt.Stmt.(*ast.RangeStmt); ok {
return nil
}
stack := make([]ast.Node, len(c.analyzeStack))
copy(stack, c.analyzeStack)
c.ContinueStmts = append(c.ContinueStmts, continueStmt{labelStmt.Stmt.(*ast.ForStmt), stack})
return nil
}
}
return nil
}
for i := len(c.analyzeStack) - 1; i >= 0; i-- {
if _, ok := c.analyzeStack[i].(*ast.RangeStmt); ok {
return nil
}
if forStmt, ok := c.analyzeStack[i].(*ast.ForStmt); ok {
stack := make([]ast.Node, len(c.analyzeStack))
copy(stack, c.analyzeStack)
c.ContinueStmts = append(c.ContinueStmts, continueStmt{forStmt, stack})
return nil
}
}
}
case *ast.CallExpr:
callTo := func(obj types.Object) {
switch o := obj.(type) {
case *types.Func:
if recv := o.Type().(*types.Signature).Recv(); recv != nil {
if _, ok := recv.Type().Underlying().(*types.Interface); ok {
c.markBlocking(c.analyzeStack)
return
}
}
if o.Pkg() != c.p.Pkg {
if c.p.IsBlocking(o) {
c.markBlocking(c.analyzeStack)
}
return
}
stack := make([]ast.Node, len(c.analyzeStack))
copy(stack, c.analyzeStack)
c.LocalCalls[o] = append(c.LocalCalls[o], stack)
case *types.Var:
c.markBlocking(c.analyzeStack)
}
}
switch f := astutil.RemoveParens(n.Fun).(type) {
case *ast.Ident:
callTo(c.p.Uses[f])
case *ast.SelectorExpr:
if sel := c.p.Selections[f]; sel != nil && typesutil.IsJsObject(sel.Recv()) {
break
}
callTo(c.p.Uses[f.Sel])
case *ast.FuncLit:
ast.Walk(c, n.Fun)
for _, arg := range n.Args {
ast.Walk(c, arg)
}
if len(c.p.FuncLitInfos[f].Blocking) != 0 {
c.markBlocking(c.analyzeStack)
}
return nil
default:
if !astutil.IsTypeExpr(f, c.p.Info) {
c.markBlocking(c.analyzeStack)
}
}
case *ast.SendStmt:
c.markBlocking(c.analyzeStack)
case *ast.UnaryExpr:
switch n.Op {
case token.AND:
if id, ok := astutil.RemoveParens(n.X).(*ast.Ident); ok {
c.p.HasPointer[c.p.Uses[id].(*types.Var)] = true
}
case token.ARROW:
c.markBlocking(c.analyzeStack)
}
case *ast.RangeStmt:
if _, ok := c.p.TypeOf(n.X).Underlying().(*types.Chan); ok {
c.markBlocking(c.analyzeStack)
}
case *ast.SelectStmt:
for _, s := range n.Body.List {
if s.(*ast.CommClause).Comm == nil { // default clause
return c
}
}
c.markBlocking(c.analyzeStack)
case *ast.CommClause:
switch comm := n.Comm.(type) {
case *ast.SendStmt:
ast.Walk(c, comm.Chan)
ast.Walk(c, comm.Value)
case *ast.ExprStmt:
ast.Walk(c, comm.X.(*ast.UnaryExpr).X)
case *ast.AssignStmt:
ast.Walk(c, comm.Rhs[0].(*ast.UnaryExpr).X)
}
for _, s := range n.Body {
ast.Walk(c, s)
}
return nil
case *ast.GoStmt:
ast.Walk(c, n.Call.Fun)
for _, arg := range n.Call.Args {
ast.Walk(c, arg)
}
return nil
case *ast.DeferStmt:
c.HasDefer = true
if funcLit, ok := n.Call.Fun.(*ast.FuncLit); ok {
ast.Walk(c, funcLit.Body)
}
}
return c
}
func (c *FuncInfo) markBlocking(stack []ast.Node) {
for _, n := range stack {
c.Blocking[n] = true
c.Flattened[n] = true
}
}