1package analysis 2 3import ( 4 "go/ast" 5 "go/token" 6 "go/types" 7 8 "github.com/gopherjs/gopherjs/compiler/astutil" 9 "github.com/gopherjs/gopherjs/compiler/typesutil" 10) 11 12type continueStmt struct { 13 forStmt *ast.ForStmt 14 analyzeStack []ast.Node 15} 16 17type Info struct { 18 *types.Info 19 Pkg *types.Package 20 IsBlocking func(*types.Func) bool 21 HasPointer map[*types.Var]bool 22 FuncDeclInfos map[*types.Func]*FuncInfo 23 FuncLitInfos map[*ast.FuncLit]*FuncInfo 24 InitFuncInfo *FuncInfo 25 allInfos []*FuncInfo 26 comments ast.CommentMap 27} 28 29type FuncInfo struct { 30 HasDefer bool 31 Flattened map[ast.Node]bool 32 Blocking map[ast.Node]bool 33 GotoLabel map[*types.Label]bool 34 LocalCalls map[*types.Func][][]ast.Node 35 ContinueStmts []continueStmt 36 p *Info 37 analyzeStack []ast.Node 38} 39 40func (info *Info) newFuncInfo() *FuncInfo { 41 funcInfo := &FuncInfo{ 42 p: info, 43 Flattened: make(map[ast.Node]bool), 44 Blocking: make(map[ast.Node]bool), 45 GotoLabel: make(map[*types.Label]bool), 46 LocalCalls: make(map[*types.Func][][]ast.Node), 47 } 48 info.allInfos = append(info.allInfos, funcInfo) 49 return funcInfo 50} 51 52func AnalyzePkg(files []*ast.File, fileSet *token.FileSet, typesInfo *types.Info, typesPkg *types.Package, isBlocking func(*types.Func) bool) *Info { 53 info := &Info{ 54 Info: typesInfo, 55 Pkg: typesPkg, 56 HasPointer: make(map[*types.Var]bool), 57 comments: make(ast.CommentMap), 58 IsBlocking: isBlocking, 59 FuncDeclInfos: make(map[*types.Func]*FuncInfo), 60 FuncLitInfos: make(map[*ast.FuncLit]*FuncInfo), 61 } 62 info.InitFuncInfo = info.newFuncInfo() 63 64 for _, file := range files { 65 for k, v := range ast.NewCommentMap(fileSet, file, file.Comments) { 66 info.comments[k] = v 67 } 68 ast.Walk(info.InitFuncInfo, file) 69 } 70 71 for { 72 done := true 73 for _, funcInfo := range info.allInfos { 74 for obj, calls := range funcInfo.LocalCalls { 75 if len(info.FuncDeclInfos[obj].Blocking) != 0 { 76 for _, call := range calls { 77 funcInfo.markBlocking(call) 78 } 79 delete(funcInfo.LocalCalls, obj) 80 done = false 81 } 82 } 83 } 84 if done { 85 break 86 } 87 } 88 89 for _, funcInfo := range info.allInfos { 90 for _, continueStmt := range funcInfo.ContinueStmts { 91 if funcInfo.Blocking[continueStmt.forStmt.Post] { 92 funcInfo.markBlocking(continueStmt.analyzeStack) 93 } 94 } 95 } 96 97 return info 98} 99 100func (c *FuncInfo) Visit(node ast.Node) ast.Visitor { 101 if node == nil { 102 if len(c.analyzeStack) != 0 { 103 c.analyzeStack = c.analyzeStack[:len(c.analyzeStack)-1] 104 } 105 return nil 106 } 107 c.analyzeStack = append(c.analyzeStack, node) 108 109 switch n := node.(type) { 110 case *ast.FuncDecl: 111 newInfo := c.p.newFuncInfo() 112 c.p.FuncDeclInfos[c.p.Defs[n.Name].(*types.Func)] = newInfo 113 return newInfo 114 case *ast.FuncLit: 115 newInfo := c.p.newFuncInfo() 116 c.p.FuncLitInfos[n] = newInfo 117 return newInfo 118 case *ast.BranchStmt: 119 switch n.Tok { 120 case token.GOTO: 121 for _, n2 := range c.analyzeStack { 122 c.Flattened[n2] = true 123 } 124 c.GotoLabel[c.p.Uses[n.Label].(*types.Label)] = true 125 case token.CONTINUE: 126 if n.Label != nil { 127 label := c.p.Uses[n.Label].(*types.Label) 128 for i := len(c.analyzeStack) - 1; i >= 0; i-- { 129 if labelStmt, ok := c.analyzeStack[i].(*ast.LabeledStmt); ok && c.p.Defs[labelStmt.Label] == label { 130 if _, ok := labelStmt.Stmt.(*ast.RangeStmt); ok { 131 return nil 132 } 133 stack := make([]ast.Node, len(c.analyzeStack)) 134 copy(stack, c.analyzeStack) 135 c.ContinueStmts = append(c.ContinueStmts, continueStmt{labelStmt.Stmt.(*ast.ForStmt), stack}) 136 return nil 137 } 138 } 139 return nil 140 } 141 for i := len(c.analyzeStack) - 1; i >= 0; i-- { 142 if _, ok := c.analyzeStack[i].(*ast.RangeStmt); ok { 143 return nil 144 } 145 if forStmt, ok := c.analyzeStack[i].(*ast.ForStmt); ok { 146 stack := make([]ast.Node, len(c.analyzeStack)) 147 copy(stack, c.analyzeStack) 148 c.ContinueStmts = append(c.ContinueStmts, continueStmt{forStmt, stack}) 149 return nil 150 } 151 } 152 } 153 case *ast.CallExpr: 154 callTo := func(obj types.Object) { 155 switch o := obj.(type) { 156 case *types.Func: 157 if recv := o.Type().(*types.Signature).Recv(); recv != nil { 158 if _, ok := recv.Type().Underlying().(*types.Interface); ok { 159 c.markBlocking(c.analyzeStack) 160 return 161 } 162 } 163 if o.Pkg() != c.p.Pkg { 164 if c.p.IsBlocking(o) { 165 c.markBlocking(c.analyzeStack) 166 } 167 return 168 } 169 stack := make([]ast.Node, len(c.analyzeStack)) 170 copy(stack, c.analyzeStack) 171 c.LocalCalls[o] = append(c.LocalCalls[o], stack) 172 case *types.Var: 173 c.markBlocking(c.analyzeStack) 174 } 175 } 176 switch f := astutil.RemoveParens(n.Fun).(type) { 177 case *ast.Ident: 178 callTo(c.p.Uses[f]) 179 case *ast.SelectorExpr: 180 if sel := c.p.Selections[f]; sel != nil && typesutil.IsJsObject(sel.Recv()) { 181 break 182 } 183 callTo(c.p.Uses[f.Sel]) 184 case *ast.FuncLit: 185 ast.Walk(c, n.Fun) 186 for _, arg := range n.Args { 187 ast.Walk(c, arg) 188 } 189 if len(c.p.FuncLitInfos[f].Blocking) != 0 { 190 c.markBlocking(c.analyzeStack) 191 } 192 return nil 193 default: 194 if !astutil.IsTypeExpr(f, c.p.Info) { 195 c.markBlocking(c.analyzeStack) 196 } 197 } 198 case *ast.SendStmt: 199 c.markBlocking(c.analyzeStack) 200 case *ast.UnaryExpr: 201 switch n.Op { 202 case token.AND: 203 if id, ok := astutil.RemoveParens(n.X).(*ast.Ident); ok { 204 c.p.HasPointer[c.p.Uses[id].(*types.Var)] = true 205 } 206 case token.ARROW: 207 c.markBlocking(c.analyzeStack) 208 } 209 case *ast.RangeStmt: 210 if _, ok := c.p.TypeOf(n.X).Underlying().(*types.Chan); ok { 211 c.markBlocking(c.analyzeStack) 212 } 213 case *ast.SelectStmt: 214 for _, s := range n.Body.List { 215 if s.(*ast.CommClause).Comm == nil { // default clause 216 return c 217 } 218 } 219 c.markBlocking(c.analyzeStack) 220 case *ast.CommClause: 221 switch comm := n.Comm.(type) { 222 case *ast.SendStmt: 223 ast.Walk(c, comm.Chan) 224 ast.Walk(c, comm.Value) 225 case *ast.ExprStmt: 226 ast.Walk(c, comm.X.(*ast.UnaryExpr).X) 227 case *ast.AssignStmt: 228 ast.Walk(c, comm.Rhs[0].(*ast.UnaryExpr).X) 229 } 230 for _, s := range n.Body { 231 ast.Walk(c, s) 232 } 233 return nil 234 case *ast.GoStmt: 235 ast.Walk(c, n.Call.Fun) 236 for _, arg := range n.Call.Args { 237 ast.Walk(c, arg) 238 } 239 return nil 240 case *ast.DeferStmt: 241 c.HasDefer = true 242 if funcLit, ok := n.Call.Fun.(*ast.FuncLit); ok { 243 ast.Walk(c, funcLit.Body) 244 } 245 } 246 return c 247} 248 249func (c *FuncInfo) markBlocking(stack []ast.Node) { 250 for _, n := range stack { 251 c.Blocking[n] = true 252 c.Flattened[n] = true 253 } 254} 255