Skip to content

Commit 5eed0f6

Browse files
committed
internal/frontend: support displaying multiple of same type
Previously on the versions page, we were not handling the case when different identifiers are added for the same type for different build contexts, and which build context was surfaced was based on chance. To support this case, it is now possible to show multiple of the same type at the same version. For example, https://pkg.go.dev/internal/poll?tab=versions at go1.10 will show: ``` type FD — windows/amd64 + func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, error) + func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, error) type FD — darwin/amd64, linux/amd64 + func (fd *FD) SetBlocking() error + func (fd *FD) WriteOnce(p []byte) (int, error) ``` For golang/go#37102 Change-Id: I19e6ef12f1f8f9c412aab7cea2782409eecf29f9 Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/317489 Trust: Julie Qiu <[email protected]> Run-TryBot: Julie Qiu <[email protected]> TryBot-Result: kokoro <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]>
1 parent 4e95d3e commit 5eed0f6

File tree

7 files changed

+197
-38
lines changed

7 files changed

+197
-38
lines changed

internal/frontend/symbol.go

Lines changed: 84 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type Symbol struct {
5555
New bool
5656
}
5757

58-
func (s *Symbol) addBuilds(builds []internal.BuildContext) {
58+
func (s *Symbol) addBuilds(builds ...internal.BuildContext) {
5959
if s.builds == nil {
6060
s.builds = map[internal.BuildContext]bool{}
6161
}
@@ -94,18 +94,23 @@ func symbolsForVersion(pkgURLPath string, symbolsAtVersion map[string]map[intern
9494
}
9595
nameToMetaToSymbol[us.Name][sm] = s
9696
}
97-
s.addBuilds(us.BuildContexts())
97+
s.addBuilds(us.BuildContexts()...)
9898
}
9999
}
100100

101101
for cm, cus := range children {
102-
// Option 1: no parent exists
103-
// - make one, add to map
104-
// - append to parent
105-
// Option 2: parent exists and supports child bc
106-
// - append to parent
107-
// Option 3 parent exists and does not support child bc
108-
// - append to parent
102+
// For each child symbol, 1 of 3 things can occur:
103+
//
104+
// Option 1: If no parent exists for this child symbol, make one
105+
// and add the parent to the map.
106+
//
107+
// Option 2: A parent exists and does not support the build context
108+
// of the child. This occurs when the parent type is introduced for
109+
// another build context, but was introduced at the previous version
110+
// for the current child. Create a new parent for this child.
111+
//
112+
// Option 3: A parent exists and does support the build context of
113+
// the child. Add the child to the parent.
109114
cs := &Symbol{
110115
Name: cm.Name,
111116
Synopsis: cm.Synopsis,
@@ -115,37 +120,43 @@ func symbolsForVersion(pkgURLPath string, symbolsAtVersion map[string]map[intern
115120
New: true,
116121
}
117122

118-
parents, ok := nameToMetaToSymbol[cm.ParentName]
119-
var found bool
120-
if ok {
121-
for _, ps := range parents {
122-
for build := range ps.builds {
123-
if cus.SupportsBuild(build) {
124-
ps.Children = append(ps.Children, cs)
125-
found = true
126-
break
127-
}
128-
}
129-
}
130-
}
131-
if found {
123+
ps := findParent(cm.ParentName, cus, nameToMetaToSymbol)
124+
if ps != nil {
125+
// Option 3: found a relevant parent.
126+
ps.Children = append(ps.Children, cs)
132127
continue
133128
}
134129

135-
// We did not find a parent, so create one.
136-
ps := createParent(cus, pkgURLPath)
137-
ps.Children = append(ps.Children, cs)
130+
// Option 1 and 2: We did not find a relevant parent, so create
131+
// one.
132+
//
133+
// Since this parent is not introduced at this version, create
134+
// a distinct type for each group of symbols.
135+
// To do so, we make up a synopsis for the SymbolMeta below, since it
136+
// is only used as a key in nameToMetaToSymbol.
137+
//
138+
// Example case:
139+
// http://pkg.go.dev/internal/poll?tab=versions for go1.10 should show:
140+
//
141+
// type FD -- windows/amd64
142+
// FD.ReadMsg
143+
// FD.WriteMsg
144+
// type FD -- darwin/amd64, linux/amd64
145+
// FD.SetBlocking
146+
// FD.WriteOnce
147+
ps = createParent(cm.ParentName, pkgURLPath, cus.BuildContexts()...)
138148
pm := internal.SymbolMeta{
139149
Name: ps.Name,
140150
ParentName: ps.Name,
141-
Synopsis: ps.Synopsis,
151+
Synopsis: fmt.Sprintf("type %s (%v)", ps.Name, cus.BuildContexts()),
142152
Section: ps.Section,
143153
Kind: ps.Kind,
144154
}
145-
ps.addBuilds(cus.BuildContexts())
146-
nameToMetaToSymbol[pm.Name] = map[internal.SymbolMeta]*Symbol{
147-
pm: ps,
155+
ps.Children = append(ps.Children, cs)
156+
if _, ok := nameToMetaToSymbol[pm.Name]; !ok {
157+
nameToMetaToSymbol[pm.Name] = map[internal.SymbolMeta]*Symbol{}
148158
}
159+
nameToMetaToSymbol[pm.Name][pm] = ps
149160
}
150161

151162
var symbols []*Symbol
@@ -163,6 +174,22 @@ func symbolsForVersion(pkgURLPath string, symbolsAtVersion map[string]map[intern
163174
return sortSymbols(symbols)
164175
}
165176

177+
func findParent(parentName string, cus *internal.UnitSymbol,
178+
nameToMetaToSymbol map[string]map[internal.SymbolMeta]*Symbol) *Symbol {
179+
parents, ok := nameToMetaToSymbol[parentName]
180+
if !ok {
181+
return nil
182+
}
183+
for _, ps := range parents {
184+
for build := range ps.builds {
185+
if cus.SupportsBuild(build) {
186+
return ps
187+
}
188+
}
189+
}
190+
return nil
191+
}
192+
166193
func symbolLink(pkgURLPath, name string, builds []internal.BuildContext) string {
167194
if len(builds) == len(internal.BuildContexts) {
168195
return fmt.Sprintf("%s#%s", pkgURLPath, name)
@@ -179,15 +206,15 @@ func symbolLink(pkgURLPath, name string, builds []internal.BuildContext) string
179206
// different version. The symbol created will have New set to false, since this
180207
// function is only used when a parent symbol is not found for the unit symbol,
181208
// which means it was not introduced at the same version.
182-
func createParent(us *internal.UnitSymbol, pkgURLPath string) *Symbol {
209+
func createParent(parentName, pkgURLPath string, builds ...internal.BuildContext) *Symbol {
183210
s := &Symbol{
184-
Name: us.ParentName,
185-
Synopsis: fmt.Sprintf("type %s", us.ParentName),
211+
Name: parentName,
212+
Synopsis: fmt.Sprintf("type %s", parentName),
186213
Section: internal.SymbolSectionTypes,
187214
Kind: internal.SymbolKindType,
188-
Link: symbolLink(pkgURLPath, us.ParentName, us.BuildContexts()),
215+
Link: symbolLink(pkgURLPath, parentName, builds),
189216
}
190-
s.addBuilds(us.BuildContexts())
217+
s.addBuilds(builds...)
191218
return s
192219
}
193220

@@ -240,10 +267,31 @@ func sortSymbols(symbols []*Symbol) [][]*Symbol {
240267

241268
func sortSymbolsGroup(syms []*Symbol) {
242269
sort.Slice(syms, func(i, j int) bool {
243-
return syms[i].Synopsis < syms[j].Synopsis
270+
s1 := syms[i]
271+
s2 := syms[j]
272+
if s1.Synopsis != s2.Synopsis {
273+
return s1.Synopsis < s2.Synopsis
274+
}
275+
return compareStringSlices(s1.Builds, s2.Builds) < 0
244276
})
245277
}
246278

279+
func compareStringSlices(ss1, ss2 []string) int {
280+
for i, s1 := range ss1 {
281+
if i >= len(ss2) { // first slice is longer, so greater
282+
return 1
283+
}
284+
if c := strings.Compare(s1, ss2[i]); c != 0 {
285+
return c
286+
}
287+
}
288+
if len(ss1) == len(ss2) {
289+
return 0
290+
}
291+
// first slice is shorter
292+
return -1
293+
}
294+
247295
// ParseVersionsDetails returns a map of versionToNameToUnitSymbol based on
248296
// data from the proovided VersionDetails.
249297
func ParseVersionsDetails(vd VersionsDetails) (_ *internal.SymbolHistory, err error) {

internal/frontend/symbol_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// 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+
5+
package frontend
6+
7+
import (
8+
"testing"
9+
)
10+
11+
func TestCompareStringSlices(t *testing.T) {
12+
a := []string{"a"}
13+
ab := []string{"a", "b"}
14+
ac := []string{"a", "c"}
15+
for _, test := range []struct {
16+
ss1, ss2 []string
17+
want int
18+
}{
19+
{nil, nil, 0},
20+
{nil, a, -1},
21+
{ab, ab, 0},
22+
{a, ab, -1},
23+
{ab, ac, -1},
24+
} {
25+
got := compareStringSlices(test.ss1, test.ss2)
26+
if got != test.want {
27+
t.Fatalf("%v, %v: got %d, want %d\n", test.ss1, test.ss2, got, test.want)
28+
}
29+
if test.want != 0 {
30+
got := compareStringSlices(test.ss2, test.ss1)
31+
want := -test.want
32+
if got != want {
33+
t.Fatalf("%v, %v: got %d, want %d\n", test.ss2, test.ss1, got, want)
34+
}
35+
}
36+
}
37+
}

internal/postgres/symbol_history.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ func GetSymbolHistoryWithPackageSymbols(ctx context.Context, ddb *database.DB,
9393
// children names are also tracked.
9494
func GetSymbolHistoryForBuildContext(ctx context.Context, ddb *database.DB, pathID int, modulePath string,
9595
bc internal.BuildContext) (_ map[string]string, err error) {
96-
defer derrors.WrapStack(&err, "getSymbolHistoryForBuildContext(ctx, ddb, %d, %q)", pathID, modulePath)
97-
defer middleware.ElapsedStat(ctx, "getSymbolHistoryForBuildContext")()
96+
defer derrors.WrapStack(&err, "GetSymbolHistoryForBuildContext(ctx, ddb, %d, %q)", pathID, modulePath)
97+
defer middleware.ElapsedStat(ctx, "GetSymbolHistoryForBuildContext")()
9898

9999
if bc == internal.BuildContextAll {
100100
bc = internal.BuildContextLinux

internal/proxy/testdata/[email protected]

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ func HelloJS() string {
107107
return "Hello"
108108
}
109109

110+
-- multigoos/multigoos.go --
111+
// +build darwin linux windows
112+
113+
package multigoos
114+
115+
// type FD is introduced for windows, linux and darwin at this version.
116+
type FD struct {}
117+
110118
-- multigoos/multigoos_windows.go --
111119
// +build windows
112120

internal/proxy/testdata/[email protected]

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ func CloseOnExec(foo string) error {
113113
return nil
114114
}
115115

116+
type FD struct {}
117+
118+
// FD was introduced in v1.1.0 for linux, darwin and windows.
119+
// MyWindowsMethod is introduced only for windows in this version.
120+
func (*FD) MyWindowsMethod() {
121+
}
122+
116123
-- multigoos/multigoos_unix.go --
117124
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
118125

@@ -122,6 +129,13 @@ func CloseOnExec(num int) (int, error) {
122129
return num, nil
123130
}
124131

132+
type FD struct {}
133+
134+
// FD was introduced in v1.1.0 for linux, darwin and windows.
135+
// MyMethod is introduced only for darwin and linux in this version.
136+
func (*FD) MyMethod() {
137+
}
138+
125139
-- multigoos/multigoos_js.go --
126140
// +build js,wasm
127141

internal/symbol.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ func (sh *SymbolHistory) GetSymbol(name, v string, build BuildContext) (_ *UnitS
140140

141141
// AddSymbol adds the given symbol to SymbolHistory.
142142
func (sh *SymbolHistory) AddSymbol(sm SymbolMeta, v string, build BuildContext) {
143+
if v == "v1.10.0" && (sm.Name == "FD" || sm.ParentName == "FD") {
144+
fmt.Println(build, v, sm.Name, sm.Synopsis)
145+
}
143146
sav, ok := sh.m[v]
144147
if !ok {
145148
sav = map[string]map[SymbolMeta]*UnitSymbol{}

internal/testing/integration/data_frontend_versions_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,44 @@ var versionsPageMultiGoos = []*frontend.VersionList{
119119
Builds: []string{"js/wasm"},
120120
},
121121
},
122+
{
123+
{
124+
Name: "FD",
125+
Synopsis: "type FD",
126+
Section: "Types",
127+
Kind: "Type",
128+
Link: "/example.com/[email protected]/multigoos?GOOS=darwin#FD",
129+
Children: []*frontend.Symbol{
130+
{
131+
Name: "FD.MyMethod",
132+
Synopsis: "func (*FD) MyMethod()",
133+
Section: "Types",
134+
Kind: "Method",
135+
Link: "/example.com/[email protected]/multigoos?GOOS=darwin#FD.MyMethod",
136+
New: true,
137+
},
138+
},
139+
Builds: []string{"darwin/amd64", "linux/amd64"},
140+
},
141+
{
142+
Name: "FD",
143+
Synopsis: "type FD",
144+
Section: "Types",
145+
Kind: "Type",
146+
Link: "/example.com/[email protected]/multigoos?GOOS=windows#FD",
147+
Children: []*frontend.Symbol{
148+
{
149+
Name: "FD.MyWindowsMethod",
150+
Synopsis: "func (*FD) MyWindowsMethod()",
151+
Section: "Types",
152+
Kind: "Method",
153+
Link: "/example.com/[email protected]/multigoos?GOOS=windows#FD.MyWindowsMethod",
154+
New: true,
155+
},
156+
},
157+
Builds: []string{"windows/amd64"},
158+
},
159+
},
122160
},
123161
},
124162
{
@@ -149,6 +187,17 @@ var versionsPageMultiGoos = []*frontend.VersionList{
149187
Builds: []string{"darwin/amd64", "linux/amd64"},
150188
},
151189
},
190+
{
191+
{
192+
Name: "FD",
193+
Synopsis: "type FD struct",
194+
Section: "Types",
195+
Kind: "Type",
196+
Link: "/example.com/[email protected]/multigoos?GOOS=darwin#FD",
197+
Builds: []string{"darwin/amd64", "linux/amd64", "windows/amd64"},
198+
New: true,
199+
},
200+
},
152201
},
153202
},
154203
},

0 commit comments

Comments
 (0)