File: escape.go

package info (click to toggle)
golang-github-gopherjs-gopherjs 0.0~git20170927.0.4152256-6
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,464 kB
  • sloc: cpp: 91; makefile: 7
file content (70 lines) | stat: -rw-r--r-- 1,532 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package analysis

import (
	"go/ast"
	"go/token"
	"go/types"
)

func EscapingObjects(n ast.Node, info *types.Info) []*types.Var {
	v := escapeAnalysis{
		info:         info,
		escaping:     make(map[*types.Var]bool),
		topScope:     info.Scopes[n],
		bottomScopes: make(map[*types.Scope]bool),
	}
	ast.Walk(&v, n)
	var list []*types.Var
	for obj := range v.escaping {
		list = append(list, obj)
	}
	return list
}

type escapeAnalysis struct {
	info         *types.Info
	escaping     map[*types.Var]bool
	topScope     *types.Scope
	bottomScopes map[*types.Scope]bool
}

func (v *escapeAnalysis) Visit(node ast.Node) (w ast.Visitor) {
	// huge overapproximation
	switch n := node.(type) {
	case *ast.UnaryExpr:
		if n.Op == token.AND {
			if _, ok := n.X.(*ast.Ident); ok {
				return &escapingObjectCollector{v}
			}
		}
	case *ast.FuncLit:
		v.bottomScopes[v.info.Scopes[n.Type]] = true
		return &escapingObjectCollector{v}
	case *ast.ForStmt:
		v.bottomScopes[v.info.Scopes[n.Body]] = true
	case *ast.RangeStmt:
		v.bottomScopes[v.info.Scopes[n.Body]] = true
	}
	return v
}

type escapingObjectCollector struct {
	analysis *escapeAnalysis
}

func (v *escapingObjectCollector) Visit(node ast.Node) (w ast.Visitor) {
	if id, ok := node.(*ast.Ident); ok {
		if obj, ok := v.analysis.info.Uses[id].(*types.Var); ok {
			for s := obj.Parent(); s != nil; s = s.Parent() {
				if s == v.analysis.topScope {
					v.analysis.escaping[obj] = true
					break
				}
				if v.analysis.bottomScopes[s] {
					break
				}
			}
		}
	}
	return v
}