Skip to content

Commit f6cb864

Browse files
committed
add cache and reverse pointer to Universe to Type
1 parent 85fd79d commit f6cb864

File tree

4 files changed

+155
-15
lines changed

4 files changed

+155
-15
lines changed

v2/parser/parse.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"path/filepath"
2727
"sort"
2828
"strings"
29+
"sync"
2930
"time"
3031

3132
"golang.org/x/tools/go/packages"
@@ -637,6 +638,13 @@ func (p *Parser) convertSignature(u types.Universe, t *gotypes.Signature) *types
637638

638639
// walkType adds the type, and any necessary child types.
639640
func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type) *types.Type {
641+
var out *types.Type
642+
defer func() {
643+
out.Universe = &u
644+
out.PointerTypeCache = &types.PointerTypeCache{
645+
Mu: sync.Mutex{},
646+
}
647+
}()
640648
// Most of the cases are underlying types of the named type.
641649
name := goNameToName(in.String())
642650
if useName != nil {
@@ -645,13 +653,13 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
645653

646654
// Handle alias types conditionally on go1.22+.
647655
// Inline this once the minimum supported version is go1.22
648-
if out := p.walkAliasType(u, in); out != nil {
656+
if out = p.walkAliasType(u, in); out != nil {
649657
return out
650658
}
651659

652660
switch t := in.(type) {
653661
case *gotypes.Struct:
654-
out := u.Type(name)
662+
out = u.Type(name)
655663
out.GoType = in
656664
if out.Kind != types.Unknown {
657665
return out
@@ -670,7 +678,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
670678
}
671679
return out
672680
case *gotypes.Map:
673-
out := u.Type(name)
681+
out = u.Type(name)
674682
out.GoType = in
675683
if out.Kind != types.Unknown {
676684
return out
@@ -680,7 +688,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
680688
out.Key = p.walkType(u, nil, t.Key())
681689
return out
682690
case *gotypes.Pointer:
683-
out := u.Type(name)
691+
out = u.Type(name)
684692
out.GoType = in
685693
if out.Kind != types.Unknown {
686694
return out
@@ -689,7 +697,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
689697
out.Elem = p.walkType(u, nil, t.Elem())
690698
return out
691699
case *gotypes.Slice:
692-
out := u.Type(name)
700+
out = u.Type(name)
693701
out.GoType = in
694702
if out.Kind != types.Unknown {
695703
return out
@@ -698,7 +706,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
698706
out.Elem = p.walkType(u, nil, t.Elem())
699707
return out
700708
case *gotypes.Array:
701-
out := u.Type(name)
709+
out = u.Type(name)
702710
out.GoType = in
703711
if out.Kind != types.Unknown {
704712
return out
@@ -708,7 +716,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
708716
out.Len = in.(*gotypes.Array).Len()
709717
return out
710718
case *gotypes.Chan:
711-
out := u.Type(name)
719+
out = u.Type(name)
712720
out.GoType = in
713721
if out.Kind != types.Unknown {
714722
return out
@@ -719,7 +727,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
719727
// cannot be properly written.
720728
return out
721729
case *gotypes.Basic:
722-
out := u.Type(types.Name{
730+
out = u.Type(types.Name{
723731
Package: "", // This is a magic package name in the Universe.
724732
Name: t.Name(),
725733
})
@@ -730,7 +738,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
730738
out.Kind = types.Unsupported
731739
return out
732740
case *gotypes.Signature:
733-
out := u.Type(name)
741+
out = u.Type(name)
734742
out.GoType = in
735743
if out.Kind != types.Unknown {
736744
return out
@@ -739,7 +747,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
739747
out.Signature = p.convertSignature(u, t)
740748
return out
741749
case *gotypes.Interface:
742-
out := u.Type(name)
750+
out = u.Type(name)
743751
out.GoType = in
744752
if out.Kind != types.Unknown {
745753
return out
@@ -758,7 +766,6 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
758766
}
759767
return out
760768
case *gotypes.Named:
761-
var out *types.Type
762769
switch t.Underlying().(type) {
763770
case *gotypes.Named, *gotypes.Basic, *gotypes.Map, *gotypes.Slice:
764771
name := goNameToName(t.String())
@@ -785,7 +792,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
785792
name.Name = fmt.Sprintf("%s[%s]", strings.SplitN(name.Name, "[", 2)[0], strings.Join(tpNames, ","))
786793
}
787794

788-
if out := u.Type(name); out.Kind != types.Unknown {
795+
if out = u.Type(name); out.Kind != types.Unknown {
789796
out.GoType = in
790797
return out // short circuit if we've already made this.
791798
}
@@ -797,7 +804,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
797804
// "feature" for users. This flattens those types
798805
// together.
799806
name := goNameToName(t.String())
800-
if out := u.Type(name); out.Kind != types.Unknown {
807+
if out = u.Type(name); out.Kind != types.Unknown {
801808
return out // short circuit if we've already made this.
802809
}
803810
out = p.walkType(u, &name, t.Underlying())
@@ -827,7 +834,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
827834
Kind: types.TypeParam,
828835
}
829836
default:
830-
out := u.Type(name)
837+
out = u.Type(name)
831838
out.GoType = in
832839
if out.Kind != types.Unknown {
833840
return out

v2/parser/parse_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,15 @@ func TestAddOnePkgToUniverse(t *testing.T) {
548548
if len(ud.Types) == 0 {
549549
t.Errorf("expected non-zero types in direct package")
550550
} else {
551+
// verify that Universe and PointerTypeCache has been populated
552+
for _, tp := range ud.Types {
553+
if tp.Universe == nil {
554+
t.Errorf("expected no nil Universe in type %v", tp)
555+
}
556+
if tp.PointerTypeCache == nil {
557+
t.Errorf("expected no nil PointerTypeCache in type %v", tp)
558+
}
559+
}
551560
type testcase struct {
552561
kind types.Kind
553562
elem string // just the type name

v2/types/types.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package types
1919
import (
2020
gotypes "go/types"
2121
"strings"
22+
"sync"
2223
)
2324

2425
// Ref makes a reference to the given type. It can only be used for e.g.
@@ -289,6 +290,13 @@ func (u Universe) Package(packagePath string) *Package {
289290
return p
290291
}
291292

293+
type PointerTypeCache struct {
294+
// mutex to protect concurrent update to the cache
295+
Mu sync.Mutex
296+
// A pointer Type for the Type
297+
PointerType *Type
298+
}
299+
292300
// Type represents a subset of possible go types.
293301
type Type struct {
294302
// There are two general categories of types, those explicitly named
@@ -364,6 +372,12 @@ type Type struct {
364372

365373
// The underlying Go type.
366374
GoType gotypes.Type
375+
376+
// Revevrse reference to Universe
377+
Universe *Universe
378+
379+
// A pointer type cache
380+
PointerTypeCache *PointerTypeCache
367381
}
368382

369383
// String returns the name of the type.
@@ -556,13 +570,41 @@ var (
556570
)
557571

558572
func PointerTo(t *Type) *Type {
559-
return &Type{
573+
if t.Universe != nil {
574+
for _, pkg := range *t.Universe {
575+
for _, tp := range pkg.Types {
576+
existingTypeName := tp.Name.String()
577+
if strings.HasPrefix(existingTypeName, "*") {
578+
if existingTypeName[1:] == t.Name.String() {
579+
return tp
580+
}
581+
}
582+
}
583+
}
584+
}
585+
cache := t.PointerTypeCache
586+
if cache != nil && cache.PointerType != nil {
587+
return cache.PointerType
588+
}
589+
out := &Type{
560590
Name: Name{
561591
Name: "*" + t.Name.String(),
562592
},
563593
Kind: Pointer,
564594
Elem: t,
565595
}
596+
// PointerTypeCache should not be empty at this point. This is a sanity processing.
597+
if cache == nil {
598+
cache = &PointerTypeCache{
599+
Mu: sync.Mutex{},
600+
}
601+
}
602+
if cache.PointerType == nil {
603+
cache.Mu.Lock()
604+
cache.PointerType = out
605+
cache.Mu.Unlock()
606+
}
607+
return out
566608
}
567609

568610
func IsInteger(t *Type) bool {

v2/types/types_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package types
1818

1919
import (
20+
"reflect"
21+
"sync"
2022
"testing"
2123
)
2224

@@ -37,6 +39,86 @@ func TestGetBuiltin(t *testing.T) {
3739
}
3840
}
3941

42+
func TestPointerTo(t *testing.T) {
43+
u := Universe{
44+
"pkgname": &Package{
45+
Types: map[string]*Type{},
46+
},
47+
}
48+
pkgInUniverse := u["pkgname"]
49+
type1 := &Type{Name: Name{Name: "structname"}, Kind: Struct}
50+
type2 := &Type{Name: Name{Name: "secondstructname"}, Kind: Struct}
51+
type3 := &Type{Name: Name{Name: "thirdstructname"}, Kind: Struct}
52+
53+
setupUniverseAndPointerTypeCache := func(tp *Type, u *Universe, pkg *Package, cached bool, existPointerTypeInUniverse bool) {
54+
tp.Universe = u
55+
tp.PointerTypeCache = &PointerTypeCache{
56+
Mu: sync.Mutex{},
57+
}
58+
if cached {
59+
tp.PointerTypeCache.PointerType = &Type{
60+
Name: Name{
61+
Name: "*" + tp.Name.String(),
62+
},
63+
Kind: Pointer,
64+
Elem: tp,
65+
}
66+
}
67+
if existPointerTypeInUniverse {
68+
pkg.Types["*"+tp.Name.String()] = &Type{
69+
Name: Name{
70+
Name: "*" + tp.Name.String(),
71+
},
72+
Kind: Pointer,
73+
Elem: tp,
74+
}
75+
}
76+
}
77+
78+
setupUniverseAndPointerTypeCache(type1, &u, pkgInUniverse, false, true)
79+
setupUniverseAndPointerTypeCache(type2, &u, pkgInUniverse, true, false)
80+
setupUniverseAndPointerTypeCache(type3, &u, pkgInUniverse, false, false)
81+
82+
testCases := []struct {
83+
name string
84+
typ *Type
85+
expected *Type
86+
expectCreation bool
87+
}{
88+
{
89+
name: "universe has the pointer type",
90+
typ: type1,
91+
expected: pkgInUniverse.Types["*"+type1.Name.String()],
92+
expectCreation: false,
93+
},
94+
{
95+
name: "cache has the pointer type",
96+
typ: type2,
97+
expected: type2.PointerTypeCache.PointerType,
98+
expectCreation: false,
99+
},
100+
{
101+
name: "cache doesn't have the pointer type",
102+
typ: type3,
103+
expected: &Type{
104+
Name: Name{Name: "*thirdstructname"},
105+
Kind: Pointer,
106+
Elem: type3,
107+
},
108+
expectCreation: true,
109+
},
110+
}
111+
for _, tc := range testCases {
112+
tp := PointerTo(tc.typ)
113+
if tc.expectCreation && !reflect.DeepEqual(tp, tc.expected) {
114+
t.Errorf("PointerTo failed, expected %v, got : %v", tc.expected, tp)
115+
}
116+
if !tc.expectCreation && tp != tc.expected {
117+
t.Errorf("PointerTo should return an existing pointer type, expected %v, got : %v", tc.expected, tp)
118+
}
119+
}
120+
}
121+
40122
func TestGetMarker(t *testing.T) {
41123
u := Universe{}
42124
n := Name{Package: "path/to/package", Name: "Foo"}

0 commit comments

Comments
 (0)