-
Notifications
You must be signed in to change notification settings - Fork 28
/
import.go
150 lines (129 loc) · 3.95 KB
/
import.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package codegen implements utilities for VDL code generators. Code
// generators for specific languages live in sub-directories.
package codegen
import (
"path"
"sort"
"strconv"
"v.io/v23/vdl"
"v.io/x/lib/set"
"v.io/x/ref/lib/vdl/compile"
)
// TODO(toddw): Remove this file, after all code generators have been updated to
// compute their own import dependencies.
// Import represents a single package import.
type Import struct {
Name string // Name of the import; may be empty.
Path string // Path of the imported package; e.g. "v.io/x/ref/lib/vdl/testdata/arith"
// Local name that refers to the imported package; either the non-empty import
// name, or the name of the imported package.
Local string
}
// Imports is a collection of package imports.
// REQUIRED: The imports must be sorted by path.
type Imports []Import
// LookupLocal returns the local name that identifies the given pkgPath.
func (x Imports) LookupLocal(pkgPath string) string {
ix := sort.Search(len(x), func(i int) bool { return x[i].Path >= pkgPath })
if ix < len(x) && x[ix].Path == pkgPath {
return x[ix].Local
}
return ""
}
// Each import must end up with a unique local name - when we see a collision we
// simply add a "_N" suffix where N starts at 2 and increments.
func uniqueImport(pkgName, pkgPath string, seen map[string]bool) Import {
name := ""
iter := 1
for {
local := pkgName
if iter > 1 {
local += "_" + strconv.Itoa(iter)
name = local
}
if !seen[local] {
// Found a unique local name - return the import.
seen[local] = true
return Import{name, pkgPath, local}
}
iter++
}
}
type pkgSorter []*compile.Package
func (s pkgSorter) Len() int { return len(s) }
func (s pkgSorter) Less(i, j int) bool { return s[i].Path < s[j].Path }
func (s pkgSorter) Swap(i, j int) { s[j], s[i] = s[i], s[j] }
// ImportsForFiles returns the imports required for the given files.
func ImportsForFiles(files ...*compile.File) Imports {
seenPath := make(map[string]bool)
pkgs := pkgSorter{}
for _, f := range files {
for _, dep := range f.PackageDeps {
if seenPath[dep.Path] {
continue
}
seenPath[dep.Path] = true
pkgs = append(pkgs, dep)
}
}
sort.Sort(pkgs)
var ret Imports
seenName := make(map[string]bool)
for _, dep := range pkgs {
ret = append(ret, uniqueImport(dep.Name, dep.Path, seenName))
}
return ret
}
// ImportsForValue returns the imports required to represent the given value v,
// from within the given pkgPath. It requires that type names used in
// v are of the form "PKGPATH.NAME".
func ImportsForValue(v *vdl.Value, pkgPath string) Imports {
deps := pkgDeps{}
deps.MergeValue(v)
var ret Imports
seen := make(map[string]bool)
for _, p := range deps.SortedPkgPaths() {
if p != pkgPath {
ret = append(ret, uniqueImport(path.Base(p), p, seen))
}
}
return ret
}
// pkgDeps maintains a set of package path dependencies.
type pkgDeps map[string]bool
func (deps pkgDeps) insertIdent(ident string) {
if pkgPath, _ := vdl.SplitIdent(ident); pkgPath != "" {
deps[pkgPath] = true
}
}
// MergeValue merges the package paths required to represent v into deps.
func (deps pkgDeps) MergeValue(v *vdl.Value) {
deps.insertIdent(v.Type().Name())
switch v.Kind() {
case vdl.Any, vdl.Union, vdl.Optional:
elem := v.Elem()
if elem != nil {
deps.MergeValue(elem)
}
case vdl.Array, vdl.List:
deps.insertIdent(v.Type().Elem().Name())
case vdl.Set:
deps.insertIdent(v.Type().Key().Name())
case vdl.Map:
deps.insertIdent(v.Type().Key().Name())
deps.insertIdent(v.Type().Elem().Name())
case vdl.Struct:
for fx := 0; fx < v.Type().NumField(); fx++ {
deps.insertIdent(v.Type().Field(fx).Type.Name())
}
}
}
// SortedPkgPaths deps as a sorted slice.
func (deps pkgDeps) SortedPkgPaths() []string {
ret := set.StringBool.ToSlice(deps)
sort.Strings(ret)
return ret
}