github.com/goplus/gossa@v0.3.25/cmd/qexp/api.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "hash/fnv" 7 "os" 8 "path/filepath" 9 "regexp" 10 "sort" 11 "strings" 12 ) 13 14 func apipath(base string) string { 15 return filepath.Join(os.Getenv("GOROOT"), "api", base) 16 } 17 18 //pkg syscall (windows-386), const CERT_E_CN_NO_MATCH = 2148204815 19 var reSym = regexp.MustCompile(`^pkg (\S+)\s?(.*)?, (?:(var|func|type|const|method))\s?(\((\*?[A-Z]\w*)\))? ([A-Z]\w*)`) 20 21 // pkg reflect, type Type interface, Align 22 var reInterface = regexp.MustCompile(`^pkg (\S+)\s?(.*)?, type ([A-Z]\w*) interface, ([A-Z]\w*)`) 23 24 // var reMethod = regexp.MustCompile(`^pkg (\S+)\s?(.*)?, method (\((\*?[A-Z]\w*)\))? ([A-Z]\w*)`) // (\(\*?[A-Z]\w*\)) ([A-Z]\w*)`) 25 // var reNum = regexp.MustCompile(`^\-?[0-9]+$`) 26 27 type ApiInfo struct { 28 Name string // name key 29 Pkg string // pkg path 30 Kind string // var|const|func|type|method| 31 MethodType string // type or empty 32 MethodName string // name or empty 33 MethodPtr string // * or empty 34 Tags []string // (os-arch-cgo) 35 Ver string // go1.15 | go1.16 or empty 36 } 37 38 func (i ApiInfo) String() string { 39 info := fmt.Sprintf("%v %v.%v", i.Kind, i.Pkg, i.Name) 40 if i.Kind == "method" { 41 info = fmt.Sprintf("%v (%v%v.%v).%v", i.Kind, i.MethodPtr, i.Pkg, i.MethodType, i.MethodName) 42 } 43 if i.Ver != "" { 44 info += " (" + i.Ver + ")" 45 } 46 if len(i.Tags) > 1 { 47 info += " " + strings.Join(i.Tags[1:], ",") 48 } 49 return info 50 } 51 52 type GoApi struct { 53 Keys map[string]*ApiInfo // name - os-arch-cgo 54 Ver string 55 } 56 57 func (c *GoApi) List() []string { 58 var list []string 59 for k, _ := range c.Keys { 60 list = append(list, k) 61 } 62 sort.Strings(list) 63 return list 64 } 65 66 func LoadApi(ver string, tagVer bool) (*GoApi, error) { 67 f, err := os.Open(apipath(ver + ".txt")) 68 if err != nil { 69 return nil, err 70 } 71 var verInfo string 72 if tagVer { 73 verInfo = ver 74 } 75 sc := bufio.NewScanner(f) 76 keys := make(map[string]*ApiInfo) 77 toTag := func(tag string) string { 78 if tag == "" { 79 return "-" 80 } 81 return tag 82 } 83 for sc.Scan() { 84 l := sc.Text() 85 has := func(v string) bool { return strings.Contains(l, v) } 86 if has("struct, ") { 87 continue 88 } 89 if has("interface, ") { 90 if m := reInterface.FindStringSubmatch(l); m != nil { 91 // m[1] pkg 92 // m[2] os-arch-cgo 93 // m[3] name 94 // m[4] method 95 pkg := m[1] 96 tag := m[2] 97 mtype := m[3] 98 mname := m[4] 99 key := pkg + "." + mtype 100 if _, ok := keys[key]; !ok { 101 keys[key] = &ApiInfo{ 102 Pkg: pkg, 103 Name: mtype, 104 Kind: "type", 105 Tags: []string{toTag(tag)}, 106 Ver: verInfo, 107 } 108 } 109 key = pkg + "." + mtype + "." + mname 110 keys[key] = &ApiInfo{ 111 Pkg: pkg, 112 Name: mtype + "." + mname, 113 Kind: "method", 114 MethodType: mtype, 115 MethodName: mname, 116 Tags: []string{toTag(tag)}, 117 Ver: verInfo, 118 } 119 } 120 continue 121 } 122 if m := reSym.FindStringSubmatch(l); m != nil { 123 // m[1] pkg 124 // m[2] os-arch-cgo 125 // m[3] var|func|type|const|method 126 // m[4] (*?Type) 127 // m[5] *?Type 128 // m[6] name 129 pkg := m[1] 130 tag := m[2] 131 if tag != "" { 132 tag = tag[1 : len(tag)-1] 133 } 134 kind := m[3] 135 mtype := m[5] 136 name := m[6] 137 var mptr string 138 var mname string 139 if kind == "method" { 140 mname = name 141 if mtype[0] == '*' { 142 mtype = mtype[1:] 143 mptr = "*" 144 } 145 name = mtype + "." + name 146 } 147 key := pkg + "." + name 148 if api, ok := keys[key]; ok { 149 if api.Tags[0] != "-" { 150 api.Tags = append(api.Tags, toTag(tag)) 151 } 152 continue 153 } 154 keys[key] = &ApiInfo{ 155 Pkg: pkg, 156 Name: name, 157 Kind: kind, 158 MethodType: mtype, 159 MethodName: mname, 160 MethodPtr: mptr, 161 Tags: []string{toTag(tag)}, 162 Ver: verInfo, 163 } 164 } 165 } 166 return &GoApi{Ver: ver, Keys: keys}, nil 167 } 168 169 type ApiCheck struct { 170 Base map[string]*ApiInfo 171 Apis []*GoApi 172 } 173 174 func NewApiCheck() *ApiCheck { 175 ac := &ApiCheck{} 176 ac.Base = make(map[string]*ApiInfo) 177 return ac 178 } 179 180 func (ac *ApiCheck) LoadBase(vers ...string) error { 181 for _, ver := range vers { 182 api, err := LoadApi(ver, false) 183 if err != nil { 184 return err 185 } 186 for k, v := range api.Keys { 187 if info, ok := ac.Base[k]; ok { 188 if info.Tags[0] != "-" { 189 info.Tags = append(info.Tags, v.Tags...) 190 } 191 } else { 192 ac.Base[k] = v 193 } 194 } 195 } 196 return nil 197 } 198 199 func (ac *ApiCheck) LoadApi(vers ...string) error { 200 for _, ver := range vers { 201 api, err := LoadApi(ver, true) 202 if err != nil { 203 return err 204 } 205 for k, v := range api.Keys { 206 if info, ok := ac.Base[k]; ok { 207 if info.Tags[0] != "-" { 208 if info.Ver != v.Ver { 209 continue 210 } 211 info.Tags = append(info.Tags, v.Tags...) 212 } 213 delete(api.Keys, k) 214 } 215 } 216 ac.Apis = append(ac.Apis, api) 217 } 218 return nil 219 } 220 221 func (ac *ApiCheck) FindApis(name string) (vers string, info *ApiInfo) { 222 for _, api := range ac.Apis { 223 if info, ok := api.Keys[name]; ok { 224 return api.Ver, info 225 } 226 } 227 return 228 } 229 230 func (ac *ApiCheck) ApiVers() (vers []string) { 231 for _, api := range ac.Apis { 232 vers = append(vers, api.Ver) 233 } 234 return 235 } 236 237 func (ac *ApiCheck) LoadPkg(pkg string) (infos []*ApiInfo) { 238 for _, api := range ac.Apis { 239 for _, v := range api.Keys { 240 if v.Pkg == pkg { 241 infos = append(infos, v) 242 } 243 } 244 } 245 for _, v := range ac.Base { 246 if v.Pkg == pkg { 247 infos = append(infos, v) 248 } 249 } 250 sort.Slice(infos, func(i, j int) bool { 251 n := strings.Compare(infos[i].Kind, infos[j].Kind) 252 if n == 0 { 253 return infos[i].Name < infos[j].Name 254 } 255 return n < 0 256 }) 257 return 258 } 259 260 type Style struct { 261 Ver string 262 Tag string 263 } 264 265 type LineData struct { 266 Ver string 267 Tag []string 268 Info string 269 } 270 271 var ( 272 osOldNews = []string{ 273 "darwin-386 darwin-386-cgo darwin-386-cgo darwin-amd64 darwin-amd64 darwin-amd64-cgo darwin-amd64-cgo", "darwin", 274 "darwin-386-cgo darwin-amd64 darwin-amd64-cgo", "darwin", 275 "darwin-386 darwin", "darwin", 276 "linux-386 linux-386-cgo linux-amd64 linux-amd64-cgo linux-arm linux-arm-cgo", "linux", 277 "linux-386-cgo linux-amd64 linux-amd64-cgo linux-arm linux-arm-cgo", "linux", 278 "linux-386 linux-386-cgo linux-amd64 linux-amd64-cgo linux-arm", "linux", 279 "linux-386-cgo linux-amd64 linux-amd64-cgo", "linux", 280 "freebsd-386 freebsd-386-cgo freebsd-amd64 freebsd-amd64-cgo freebsd-arm freebsd-arm-cgo", "freebsd", 281 "freebsd-386-cgo freebsd-amd64 freebsd-amd64-cgo freebsd-arm freebsd-arm-cgo", "freebsd", 282 "freebsd-386 freebsd-amd64", "freebsd", 283 "netbsd-386 netbsd-386-cgo netbsd-amd64 netbsd-amd64-cgo netbsd-arm netbsd-arm-cgo netbsd-arm64 netbsd-arm64-cgo", "netbsd", 284 "netbsd-386-cgo netbsd-amd64 netbsd-amd64-cgo netbsd-arm netbsd-arm-cgo netbsd-arm64 netbsd-arm64-cgo", "netbsd", 285 "netbsd-386 netbsd-386-cgo netbsd-amd64 netbsd-amd64-cgo netbsd-arm netbsd-arm-cgo", "netbsd", 286 "openbsd-386 openbsd-386-cgo openbsd-amd64 openbsd-amd64-cgo", "openbsd", 287 "windows-386 windows-amd64", "windows", 288 } 289 osReplacer = strings.NewReplacer(osOldNews...) 290 ) 291 292 func osReplace(s string) string { 293 for i := 0; i < len(osOldNews); i += 2 { 294 s = strings.ReplaceAll(s, osOldNews[i], osOldNews[i+1]) 295 } 296 return s 297 } 298 299 var ( 300 tagsName = make(map[string]string) 301 tagIndex int 302 ) 303 304 func (d LineData) TagName() (name string, tags []string) { 305 if d.Ver != "" { 306 name += "_" + strings.Replace(d.Ver, ".", "", -1) 307 tags = append(tags, "// +build "+d.Ver) 308 } 309 switch len(d.Tag) { 310 case 0: 311 case 1: 312 name += "_" + strings.Replace(d.Tag[0], "-", "_", -1) 313 default: 314 otags := strings.Join(d.Tag, " ") 315 ntags := osReplace(otags) 316 if strings.Index(ntags, " ") == -1 { 317 name += "_" + ntags 318 } else { 319 tags = append(tags, "// +build "+strings.Replace(ntags, "-", ",", -1)) 320 if tag, ok := tagsName[ntags]; ok { 321 name += "_" + tag 322 } else { 323 h := fnv.New32() 324 h.Write([]byte(ntags)) 325 name += fmt.Sprintf("_%v", h.Sum32()) 326 } 327 } 328 } 329 return 330 } 331 332 type File struct { 333 Id string 334 Name string 335 Tags []string 336 ExtList []string 337 TypList []string 338 } 339 340 func (ac *ApiCheck) Export(pkgPath string) (map[string]*File, error) { 341 extList, typList, ok := ac.ExportData(pkgPath) 342 if !ok { 343 return nil, nil 344 } 345 fileMap := make(map[string]*File) 346 for _, v := range extList { 347 name, tags := v.TagName() 348 f, ok := fileMap[name] 349 if !ok { 350 f = &File{Name: name, Tags: tags} 351 fileMap[name] = f 352 } 353 f.ExtList = append(f.ExtList, v.Info) 354 } 355 for _, v := range typList { 356 name, tags := v.TagName() 357 f, ok := fileMap[name] 358 if !ok { 359 f = &File{Name: name, Tags: tags} 360 fileMap[name] = f 361 } 362 f.TypList = append(f.TypList, v.Info) 363 } 364 return fileMap, nil 365 } 366 367 func cleanTags(tags []string) []string { 368 sort.Strings(tags) 369 var i, j int 370 for { 371 if i >= len(tags)-1 { 372 break 373 } 374 for j = i + 1; j < len(tags) && tags[i] == tags[j]; j++ { 375 } 376 tags = append(tags[:i+1], tags[j:]...) 377 i++ 378 } 379 return tags 380 } 381 382 // linux-386-cgo linux-amd64 linux-amd64-cgo linux-arm linux-arm-cgo 383 // darwin-386-cgo darwin-amd64 darwin-amd64-cgo 384 // freebsd-386 freebsd-386-cgo freebsd-amd64 freebsd-amd64-cgo freebsd-arm freebsd-arm-cgo 385 // netbsd-386 netbsd-386-cgo netbsd-amd64 netbsd-amd64-cgo netbsd-arm netbsd-arm-cgo netbsd-arm64 netbsd-arm64-cgo 386 // openbsd-386 openbsd-386-cgo openbsd-amd64 openbsd-amd64-cgo 387 // windows-386 windows-amd64 388 func (ac *ApiCheck) ExportData(pkgPath string) (extList []LineData, typList []LineData, ok bool) { 389 infos := ac.LoadPkg(pkgPath) 390 if len(infos) == 0 { 391 return 392 } 393 pkgList := strings.Split(pkgPath, "/") 394 pkgName := pkgList[len(pkgList)-1] 395 for _, t := range infos { 396 tags := cleanTags(t.Tags)[1:] 397 switch t.Kind { 398 case "var": 399 extList = append(extList, LineData{ 400 t.Ver, 401 tags, 402 fmt.Sprintf("%q : &%v", pkgPath+"."+t.Name, pkgName+"."+t.Name), 403 }) 404 case "func": 405 extList = append(extList, LineData{ 406 t.Ver, 407 tags, 408 fmt.Sprintf("%q : %v", pkgPath+"."+t.Name, pkgName+"."+t.Name), 409 }) 410 case "type": 411 typList = append(typList, LineData{ 412 t.Ver, 413 tags, 414 fmt.Sprintf("(*%v.%v)(nil)", pkgName, t.Name), 415 }) 416 for _, v := range infos { 417 if v.Kind == "method" { 418 if v.MethodType == t.Name { 419 tags := v.Tags[1:] 420 sort.Strings(tags) 421 extList = append(extList, LineData{ 422 v.Ver, 423 tags, 424 fmt.Sprintf("\"(%v%v.%v).%v\" : (%v%v.%v).%v", 425 v.MethodPtr, pkgPath, v.MethodType, v.MethodName, 426 v.MethodPtr, pkgName, v.MethodType, v.MethodName), 427 }) 428 } 429 } 430 } 431 } 432 } 433 ok = len(extList) > 0 || len(typList) > 0 434 return 435 }