gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/nogo/facts/facts.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package facts implements alternate fact types. 16 package facts 17 18 import ( 19 "encoding/gob" 20 "fmt" 21 "go/types" 22 "io" 23 "reflect" 24 "sort" 25 26 "archive/zip" 27 "golang.org/x/tools/go/analysis" 28 "golang.org/x/tools/go/types/objectpath" 29 ) 30 31 // Serializer is used for fact serialization. 32 // 33 // It generalizes over the Package and Bundle types. 34 type Serializer interface { 35 Serialize(w io.Writer) error 36 } 37 38 // item is used for serialiation. 39 type item struct { 40 Key string 41 Value any 42 } 43 44 // writeItems is an implementation of Serialize. 45 // 46 // This will sort the list as a side effect. 47 func writeItems(w io.Writer, is []item) error { 48 sort.Slice(is, func(i, j int) bool { 49 return is[i].Key < is[j].Key 50 }) 51 enc := gob.NewEncoder(w) 52 return enc.Encode(is) 53 } 54 55 // readItems is an implementation of io.ReaderTo.ReadTo. 56 func readItems(r io.Reader) (is []item, err error) { 57 dec := gob.NewDecoder(r) 58 err = dec.Decode(&is) 59 return 60 } 61 62 // Package is a set of facts about a single package. 63 // 64 // These use the types.Object as the key because this is canonical. Normally, 65 // this is canonical only in the context of a single types.Package. However, 66 // because all imports are shared across all packages, there is a single 67 // canonical types.Object shared among all packages being analyzed. 68 type Package struct { 69 Objects map[types.Object][]analysis.Fact 70 } 71 72 // NewPackage returns a new set of Package facts. 73 func NewPackage() *Package { 74 return &Package{ 75 Objects: make(map[types.Object][]analysis.Fact), 76 } 77 } 78 79 func extractObjectpath(obj types.Object) (name objectpath.Path, err error) { 80 defer func() { 81 // Unfortunately, objectpath.For will occasionally panic for 82 // certain objects. This happens with basic analysis packages 83 // (buildssa), and therefore cannot be avoided. 84 if r := recover(); r != nil { 85 err = fmt.Errorf("panic: %v", r) 86 } 87 }() 88 // Allow empty name for no object. 89 if obj != nil { 90 name, err = objectpath.For(obj) 91 } 92 return 93 } 94 95 // Serialize implements Serializer.Serialize. 96 func (p *Package) Serialize(w io.Writer) error { 97 is := make([]item, 0, len(p.Objects)) 98 for obj, facts := range p.Objects { 99 name, err := extractObjectpath(obj) 100 if err != nil { 101 continue // Not exported; expected. 102 } 103 is = append(is, item{ 104 Key: string(name), 105 Value: facts, 106 }) 107 } 108 return writeItems(w, is) 109 } 110 111 // ReadFrom deserializes a package. 112 func (p *Package) ReadFrom(pkg *types.Package, r io.Reader) error { 113 is, err := readItems(r) 114 if err != nil { 115 return err 116 } 117 for _, fi := range is { 118 var ( 119 obj types.Object 120 err error 121 ) 122 if fi.Key != "" { 123 obj, err = objectpath.Object(pkg, objectpath.Path(fi.Key)) 124 } 125 if err != nil { 126 // This could simply be a fact saved on an unexported 127 // object. We just suppress this error and ignore it. 128 continue 129 } 130 p.Objects[obj] = fi.Value.([]analysis.Fact) 131 } 132 return nil 133 } 134 135 // ExportFact exports an object fact. 136 func (p *Package) ExportFact(obj types.Object, ptr analysis.Fact) { 137 for i, v := range p.Objects[obj] { 138 if reflect.TypeOf(v) == reflect.TypeOf(ptr) { 139 p.Objects[obj][i] = ptr // Replace. 140 return 141 } 142 } 143 // Append this new fact. 144 p.Objects[obj] = append(p.Objects[obj], ptr) 145 } 146 147 // ImportFact imports an object fact. 148 func (p *Package) ImportFact(obj types.Object, ptr analysis.Fact) bool { 149 if p == nil { 150 return false // No facts. 151 } 152 for _, v := range p.Objects[obj] { 153 if reflect.TypeOf(v) == reflect.TypeOf(ptr) { 154 // Set the value to the element saved in our facts. 155 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem()) 156 return true 157 } 158 } 159 return false 160 } 161 162 // Bundle is a set of facts about different packages. 163 // 164 // This is used to serialize a collection of facts about different packages, 165 // which will be loaded and evaluated lazily. 166 type Bundle struct { 167 reader *zip.ReadCloser 168 decoded map[string]*Package 169 } 170 171 // NewBundle makes a new package bundle. 172 func NewBundle() *Bundle { 173 return &Bundle{ 174 decoded: make(map[string]*Package), 175 } 176 } 177 178 // Serialize implements Serializer.Serialize. 179 func (b *Bundle) Serialize(w io.Writer) error { 180 zw := zip.NewWriter(w) 181 for pkg, facts := range b.decoded { 182 if facts == nil { 183 // Some facts may be omitted for bundles, if there is 184 // only type information but no source information. We 185 // omit these completely from the serialized bundle. 186 continue 187 } 188 if len(facts.Objects) == 0 { 189 // Similarly prevent serializing any Packages that have 190 // no facts associated with them. This will speed up 191 // deserialization since the Package can handle nil. 192 continue 193 } 194 wc, err := zw.Create(pkg) 195 if err != nil { 196 return err 197 } 198 if err := facts.Serialize(wc); err != nil { 199 return err 200 } 201 } 202 return zw.Close() 203 } 204 205 // BundleFrom may be used to create a new bundle that deserializes the contents 206 // of the given file. 207 // 208 // Note that there is no explicit close mechanism, and the underlying file will 209 // be closed only when the object is finalized. 210 func BundleFrom(filename string) (*Bundle, error) { 211 r, err := zip.OpenReader(filename) 212 if err != nil { 213 return nil, err 214 } 215 return &Bundle{ 216 reader: r, 217 decoded: make(map[string]*Package), 218 }, nil 219 } 220 221 // Add adds the package to the Bundle. 222 func (b *Bundle) Add(path string, facts *Package) { 223 b.decoded[path] = facts 224 } 225 226 // Package looks up the given package in the bundle. 227 func (b *Bundle) Package(pkg *types.Package) (*Package, error) { 228 // Already decoded? 229 if facts, ok := b.decoded[pkg.Path()]; ok { 230 return facts, nil 231 } 232 233 if b.reader == nil { 234 // Nothing available. 235 // 236 // N.B. some bundles contain only cached packages. 237 return nil, nil 238 } 239 240 // Find based on the reader. 241 for _, f := range b.reader.File { 242 if f.Name != pkg.Path() { 243 continue 244 } 245 246 // Extract from the archive. 247 facts := NewPackage() 248 rc, err := f.Open() 249 if err != nil { 250 return nil, err 251 } 252 defer rc.Close() 253 if err := facts.ReadFrom(pkg, rc); err != nil { 254 return nil, err 255 } 256 257 // Memoize the result. 258 b.Add(pkg.Path(), facts) 259 return facts, nil 260 } 261 262 // Nothing available. 263 return nil, fmt.Errorf("no facts available for package %q", pkg.Path()) 264 } 265 266 func init() { 267 gob.Register((*item)(nil)) 268 gob.Register(([]analysis.Fact)(nil)) 269 }