1
2
3
4
5
6
7
8
9
10
11 package main
12
13 import (
14 "bytes"
15 "cmp"
16 "errors"
17 "fmt"
18 "go/format"
19 "go/types"
20 "io/fs"
21 "log"
22 "os"
23 "path/filepath"
24 "regexp"
25 "runtime"
26 "slices"
27 "strings"
28
29 "golang.org/x/tools/go/packages"
30 )
31
32 func main() {
33
34 symRE := regexp.MustCompile(`^pkg (\S+).*?, (var|func|type|const|method \([^)]*\)) ([A-Z]\w*)(.*)`)
35 pkgs := make(map[string]map[string]symInfo)
36 for minor := 0; ; minor++ {
37 base := "go1.txt"
38 if minor > 0 {
39 base = fmt.Sprintf("go1.%d.txt", minor)
40 }
41 filename := filepath.Join(runtime.GOROOT(), "api", base)
42 data, err := os.ReadFile(filename)
43 if err != nil {
44 if errors.Is(err, fs.ErrNotExist) {
45 break
46 }
47 log.Fatal(err)
48 }
49
50
51 for linenum, line := range strings.Split(string(data), "\n") {
52 if line == "" || strings.HasPrefix(line, "#") {
53 continue
54 }
55 m := symRE.FindStringSubmatch(line)
56 if m == nil {
57 log.Fatalf("invalid input: %s:%d: %s", filename, linenum+1, line)
58 }
59 path, kind, sym, rest := m[1], m[2], m[3], m[4]
60
61 if _, recv, ok := strings.Cut(kind, "method "); ok {
62
63 kind = "method"
64
65 recv := removeTypeParam(recv)
66
67 sym = recv + "." + sym
68
69 } else if _, field, ok := strings.Cut(rest, " struct, "); ok && kind == "type" {
70
71 kind = "field"
72 name, typ, _ := strings.Cut(field, " ")
73
74
75
76
77 if name == "embedded" {
78
79 typ = strings.TrimPrefix(typ, "*")
80 if _, after, ok := strings.Cut(typ, "."); ok {
81 typ = after
82 }
83 typ = removeTypeParam(typ)
84 name = typ
85 }
86
87 sym += "." + name
88 }
89
90 symbols, ok := pkgs[path]
91 if !ok {
92 symbols = make(map[string]symInfo)
93 pkgs[path] = symbols
94 }
95
96
97
98
99
100 if _, ok := symbols[sym]; !ok {
101 symbols[sym] = symInfo{kind, minor}
102 }
103 }
104 }
105
106
107
108 pkgs["syscall/js"] = loadSymbols("syscall/js", "GOOS=js", "GOARCH=wasm")
109 pkgs["unsafe"] = exportedSymbols(types.Unsafe)
110
111
112 var buf bytes.Buffer
113 buf.WriteString(`// Copyright 2024 The Go Authors. All rights reserved.
114 // Use of this source code is governed by a BSD-style
115 // license that can be found in the LICENSE file.
116
117 // Code generated by generate.go. DO NOT EDIT.
118
119 package stdlib
120
121 var PackageSymbols = map[string][]Symbol{
122 `)
123
124 for _, path := range sortedKeys(pkgs) {
125 pkg := pkgs[path]
126 fmt.Fprintf(&buf, "\t%q: {\n", path)
127 for _, name := range sortedKeys(pkg) {
128 info := pkg[name]
129 fmt.Fprintf(&buf, "\t\t{%q, %s, %d},\n",
130 name, strings.Title(info.kind), info.minor)
131 }
132 fmt.Fprintln(&buf, "},")
133 }
134 fmt.Fprintln(&buf, "}")
135 fmtbuf, err := format.Source(buf.Bytes())
136 if err != nil {
137 log.Fatal(err)
138 }
139 if err := os.WriteFile("manifest.go", fmtbuf, 0666); err != nil {
140 log.Fatal(err)
141 }
142 }
143
144 type symInfo struct {
145 kind string
146 minor int
147 }
148
149
150
151 func loadSymbols(pkg string, extraEnv ...string) map[string]symInfo {
152 pkgs, err := packages.Load(&packages.Config{
153 Mode: packages.NeedTypes,
154 Env: append(os.Environ(), extraEnv...),
155 }, pkg)
156 if err != nil {
157 log.Fatalln(err)
158 } else if len(pkgs) != 1 {
159 log.Fatalf("got %d packages, want one package %q", len(pkgs), pkg)
160 }
161 return exportedSymbols(pkgs[0].Types)
162 }
163
164 func exportedSymbols(pkg *types.Package) map[string]symInfo {
165 symbols := make(map[string]symInfo)
166 for _, name := range pkg.Scope().Names() {
167 if obj := pkg.Scope().Lookup(name); obj.Exported() {
168 var kind string
169 switch obj.(type) {
170 case *types.Func, *types.Builtin:
171 kind = "func"
172 case *types.Const:
173 kind = "const"
174 case *types.Var:
175 kind = "var"
176 case *types.TypeName:
177 kind = "type"
178
179 default:
180 log.Fatalf("unexpected object type: %v", obj)
181 }
182 symbols[name] = symInfo{kind: kind, minor: 0}
183 }
184 }
185 return symbols
186 }
187
188 func sortedKeys[M ~map[K]V, K cmp.Ordered, V any](m M) []K {
189 r := make([]K, 0, len(m))
190 for k := range m {
191 r = append(r, k)
192 }
193 slices.Sort(r)
194 return r
195 }
196
197 func removeTypeParam(s string) string {
198 i := strings.IndexByte(s, '[')
199 j := strings.LastIndexByte(s, ']')
200 if i > 0 && j > i {
201 s = s[:i] + s[j+len("["):]
202 }
203 return s
204 }
205
View as plain text