Skip to content
This repository has been archived by the owner on Sep 20, 2022. It is now read-only.

Commit

Permalink
Fix bug with collisions in safe string names
Browse files Browse the repository at this point in the history
  • Loading branch information
albrow committed Jul 2, 2018
1 parent 792cc6b commit 3442a58
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 13 deletions.
17 changes: 10 additions & 7 deletions transform/safe_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ var safeSymbolMap = map[string]string{
" ": "_",
}

// safeStringCache is a mapping of unsafe type strings to safe type strings. It
// reduces duplicate work and prevents safe string collissions.
var safeStringCache map[string]string = map[string]string{}
// unsafeToSafe is a mapping of unsafe type strings to safe type strings.
var unsafeToSafe map[string]string = map[string]string{}

// safeToUnsafe is a mapping of safe type strings to unsafe type strings.
var safeToUnsafe map[string]string = map[string]string{}

func typeToSafeString(typ types.Type) string {
return exprToSafeString(typeToExpr(typ))
Expand All @@ -35,27 +37,28 @@ func typeToSafeString(typ types.Type) string {
// TODO(albrow): This could be optimized.
func replaceUnsafeSymbols(unsafe string) string {
unsafe = strings.TrimSpace(unsafe)
if safe, found := safeStringCache[unsafe]; found {
if safe, found := unsafeToSafe[unsafe]; found {
return safe
}
safe := unsafe
for unsafeSymbol, safeSymbol := range safeSymbolMap {
safe = strings.Replace(safe, unsafeSymbol, safeSymbol, -1)
}
if _, found := safeStringCache[safe]; found {
if _, found := safeToUnsafe[safe]; found {
// The safe string collides with another safe string that we have generated.
// We need to append a counter to make it unique.
safe = appendSafeStringCounter(safe)
}
safeStringCache[unsafe] = safe
unsafeToSafe[unsafe] = safe
safeToUnsafe[safe] = unsafe
return safe
}

// TODO(albrow): This could be optimized.
func appendSafeStringCounter(s string) string {
for i := 0; i < 100; i++ {
stringWithCounter := fmt.Sprintf("%s_%d", s, i)
if _, found := safeStringCache[stringWithCounter]; !found {
if _, found := safeToUnsafe[stringWithCounter]; !found {
return stringWithCounter
}
}
Expand Down
52 changes: 52 additions & 0 deletions transform/transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,58 @@ func main() {
testParseFile(t, src, expected)
}

func TestTransformSafeStringCollisions(t *testing.T) {
src := `package main
type Box[T] struct{
val T
}
func main() {
var _ = Box[**string]{}
var _ = Box[[]string]{}
var _ = Box[****string]{}
var _ = Box[[]**string]{}
var _ = Box[**[]string]{}
var _ = Box[[][]string]{}
}
`

expected := `package main
type (
Box______string struct {
val ****string
}
Box______string_0 struct {
val []**string
}
Box______string_1 struct {
val **[]string
}
Box______string_2 struct {
val [][]string
}
Box____string struct {
val []string
}
Box____string_0 struct {
val **string
}
)
func main() {
var _ = Box____string_0{}
var _ = Box____string{}
var _ = Box______string{}
var _ = Box______string_0{}
var _ = Box______string_1{}
var _ = Box______string_2{}
}
`
testParseFile(t, src, expected)
}

func testParseFile(t *testing.T, src string, expected string) {
t.Helper()
fset := token.NewFileSet()
Expand Down
17 changes: 11 additions & 6 deletions types/generics.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ type typeArg struct {
}

type GenericDecl struct {
Name string
Type GenericType
Usages map[string]ConcreteType
Name string
Type GenericType
Usages []ConcreteType
seenUsages map[string]struct{}
}

func addGenericDecl(obj Object, typ GenericType) {
Expand All @@ -78,10 +79,14 @@ func addGenericUsage(genObj Object, typ ConcreteType) {
// TODO(albrow): can we avoid panicking here?
panic(fmt.Errorf("declaration not found for generic object %s (%s)", dk, genObj.Id()))
}
if genDecl.Usages == nil {
genDecl.Usages = map[string]ConcreteType{}
if genDecl.seenUsages == nil {
genDecl.seenUsages = map[string]struct{}{}
}
uk := usageKey(typ.TypeMap())
if _, seen := genDecl.seenUsages[uk]; !seen {
genDecl.Usages = append(genDecl.Usages, typ)
genDecl.seenUsages[uk] = struct{}{}
}
genDecl.Usages[usageKey(typ.TypeMap())] = typ
}

func declKey(typ GenericType) string {
Expand Down

0 comments on commit 3442a58

Please sign in to comment.