blob: 64e26f1a479ae88188e43f97e9117ee13acbe7b0 [file] [log] [blame]
Zvonimir Pavlinovicf6327c52021-05-26 15:42:08 -07001// Copyright 2021 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package vta
6
7import (
Tim King4a2dd0d2023-05-04 15:31:19 -07008 "bytes"
Tim Kingf367f012021-07-30 15:39:57 -07009 "fmt"
Zvonimir Pavlinovicf6327c52021-05-26 15:42:08 -070010 "go/ast"
11 "go/parser"
Peter Weinbergr559c4302023-09-12 10:11:42 -040012 "os"
Tim Kingf367f012021-07-30 15:39:57 -070013 "sort"
Zvonimir Pavlinovicf6327c52021-05-26 15:42:08 -070014 "strings"
Tim King4a2dd0d2023-05-04 15:31:19 -070015 "testing"
Zvonimir Pavlinovicf6327c52021-05-26 15:42:08 -070016
Tim Kingf367f012021-07-30 15:39:57 -070017 "golang.org/x/tools/go/callgraph"
Zvonimir Pavlinovicf6327c52021-05-26 15:42:08 -070018 "golang.org/x/tools/go/ssa/ssautil"
19
20 "golang.org/x/tools/go/loader"
21 "golang.org/x/tools/go/ssa"
22)
23
24// want extracts the contents of the first comment
25// section starting with "WANT:\n". The returned
26// content is split into lines without // prefix.
27func want(f *ast.File) []string {
28 for _, c := range f.Comments {
29 text := strings.TrimSpace(c.Text())
30 if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
31 return strings.Split(t, "\n")
32 }
33 }
34 return nil
35}
36
37// testProg returns an ssa representation of a program at
38// `path`, assumed to define package "testdata," and the
39// test want result as list of strings.
Zvonimir Pavlinovicdcaea062022-04-25 15:38:59 -070040func testProg(path string, mode ssa.BuilderMode) (*ssa.Program, []string, error) {
Peter Weinbergr559c4302023-09-12 10:11:42 -040041 content, err := os.ReadFile(path)
Zvonimir Pavlinovicf6327c52021-05-26 15:42:08 -070042 if err != nil {
43 return nil, nil, err
44 }
45
46 conf := loader.Config{
47 ParserMode: parser.ParseComments,
48 }
49
50 f, err := conf.ParseFile(path, content)
51 if err != nil {
52 return nil, nil, err
53 }
54
55 conf.CreateFromFiles("testdata", f)
56 iprog, err := conf.Load()
57 if err != nil {
58 return nil, nil, err
59 }
60
Zvonimir Pavlinovicdcaea062022-04-25 15:38:59 -070061 prog := ssautil.CreateProgram(iprog, mode)
Zvonimir Pavlinovicf6327c52021-05-26 15:42:08 -070062 // Set debug mode to exercise DebugRef instructions.
63 prog.Package(iprog.Created[0].Pkg).SetDebugMode(true)
64 prog.Build()
65 return prog, want(f), nil
66}
67
68func firstRegInstr(f *ssa.Function) ssa.Value {
69 for _, b := range f.Blocks {
70 for _, i := range b.Instrs {
71 if v, ok := i.(ssa.Value); ok {
72 return v
73 }
74 }
75 }
76 return nil
77}
78
79// funcName returns a name of the function `f`
80// prefixed with the name of the receiver type.
81func funcName(f *ssa.Function) string {
82 recv := f.Signature.Recv()
83 if recv == nil {
84 return f.Name()
85 }
86 tp := recv.Type().String()
87 return tp[strings.LastIndex(tp, ".")+1:] + "." + f.Name()
88}
Tim Kingf367f012021-07-30 15:39:57 -070089
90// callGraphStr stringifes `g` into a list of strings where
91// each entry is of the form
Russ Coxad8ef152022-04-11 22:39:42 -040092//
93// f: cs1 -> f1, f2, ...; ...; csw -> fx, fy, ...
94//
Tim Kingf367f012021-07-30 15:39:57 -070095// f is a function, cs1, ..., csw are call sites in f, and
96// f1, f2, ..., fx, fy, ... are the resolved callees.
97func callGraphStr(g *callgraph.Graph) []string {
98 var gs []string
99 for f, n := range g.Nodes {
100 c := make(map[string][]string)
101 for _, edge := range n.Out {
Alan Donovanae524772024-06-10 11:18:14 -0400102 cs := edge.Site.String() // TODO(adonovan): handle Site=nil gracefully
Tim Kingf367f012021-07-30 15:39:57 -0700103 c[cs] = append(c[cs], funcName(edge.Callee.Func))
104 }
105
106 var cs []string
107 for site, fs := range c {
108 sort.Strings(fs)
109 entry := fmt.Sprintf("%v -> %v", site, strings.Join(fs, ", "))
110 cs = append(cs, entry)
111 }
112
113 sort.Strings(cs)
114 entry := fmt.Sprintf("%v: %v", funcName(f), strings.Join(cs, "; "))
115 gs = append(gs, entry)
116 }
117 return gs
118}
Tim King4a2dd0d2023-05-04 15:31:19 -0700119
120// Logs the functions of prog to t.
121func logFns(t testing.TB, prog *ssa.Program) {
122 for fn := range ssautil.AllFunctions(prog) {
123 var buf bytes.Buffer
124 fn.WriteTo(&buf)
125 t.Log(buf.String())
126 }
127}