github.com/goplus/gogen@v1.16.0/import.go (about) 1 /* 2 Copyright 2021 The GoPlus Authors (goplus.org) 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 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 14 package gogen 15 16 import ( 17 "fmt" 18 "go/ast" 19 "go/constant" 20 "go/token" 21 "go/types" 22 "log" 23 "path" 24 "strconv" 25 "strings" 26 ) 27 28 // ---------------------------------------------------------------------------- 29 30 // Ref type 31 type Ref = types.Object 32 33 // A PkgRef describes a Go package imported by others. 34 type PkgRef struct { 35 // Types provides type information for the package. 36 // The NeedTypes LoadMode bit sets this field for packages matching the 37 // patterns; type information for dependencies may be missing or incomplete, 38 // unless NeedDeps and NeedImports are also set. 39 Types *types.Package 40 } 41 42 func (p PkgRef) isValid() bool { 43 return p.Types != nil 44 } 45 46 func (p PkgRef) isNil() bool { 47 return p.Types == nil 48 } 49 50 // Path returns the package path. 51 func (p PkgRef) Path() string { 52 return p.Types.Path() 53 } 54 55 // Ref returns the object in this package with the given name if such an 56 // object exists; otherwise it panics. 57 func (p PkgRef) Ref(name string) Ref { 58 if o := p.TryRef(name); o != nil { 59 return o 60 } 61 panic(p.Path() + "." + name + " not found") 62 } 63 64 // TryRef returns the object in this package with the given name if such an 65 // object exists; otherwise it returns nil. 66 func (p PkgRef) TryRef(name string) Ref { 67 return p.Types.Scope().Lookup(name) 68 } 69 70 // MarkForceUsed marks to import a package always (i.e. `import _ pkgPath`). 71 func (p PkgRef) MarkForceUsed(pkg *Package) { 72 pkg.file.forceImport(p.Types.Path()) 73 } 74 75 // Deprecated: EnsureImported is nothing to do now. 76 func (p PkgRef) EnsureImported() { 77 } 78 79 func isGopoConst(name string) bool { 80 return strings.HasPrefix(name, gopoPrefix) 81 } 82 83 func isGopFunc(name string) bool { 84 return isOverload(name) || isGopCommon(name) 85 } 86 87 func isOverload(name string) bool { 88 n := len(name) 89 return n > 3 && name[n-3:n-1] == "__" 90 } 91 92 // Gop?_xxx 93 func isGopCommon(name string) bool { 94 const n = len(commonPrefix) 95 return len(name) > n+2 && name[n+1] == '_' && name[:n] == commonPrefix 96 } 97 98 // InitThisGopPkg initializes a Go+ package. 99 func InitThisGopPkg(pkg *types.Package) { 100 InitThisGopPkgEx(pkg, nil) 101 } 102 103 // InitThisGopPkg initializes a Go+ package. pos map overload name to postion. 104 func InitThisGopPkgEx(pkg *types.Package, pos map[string]token.Pos) { 105 scope := pkg.Scope() 106 gopos := make([]string, 0, 4) 107 overloads := make(map[omthd][]types.Object) 108 onameds := make(map[string][]*types.Named) 109 names := scope.Names() 110 for _, name := range names { 111 if isGopoConst(name) { 112 gopos = append(gopos, name) 113 continue 114 } 115 o := scope.Lookup(name) 116 if tn, ok := o.(*types.TypeName); ok && tn.IsAlias() { 117 continue 118 } 119 if named, ok := o.Type().(*types.Named); ok { 120 var list methodList 121 switch t := named.Underlying().(type) { 122 case *types.Interface: 123 list = t // add interface overload method to named 124 default: 125 list = named 126 } 127 for i, n := 0, list.NumMethods(); i < n; i++ { 128 m := list.Method(i) 129 mName := m.Name() 130 if isOverload(mName) { // overload method 131 mthd := mName[:len(mName)-3] 132 key := omthd{named, mthd} 133 overloads[key] = append(overloads[key], m) 134 } 135 } 136 if isOverload(name) { // overload named 137 key := name[:len(name)-3] 138 onameds[key] = append(onameds[key], named) 139 } 140 } else if isOverload(name) { // overload function 141 key := omthd{nil, name[:len(name)-3]} 142 overloads[key] = append(overloads[key], o) 143 } else { 144 checkGoptsx(pkg, scope, name, o) 145 } 146 } 147 for _, gopoName := range gopos { 148 if names, ok := checkOverloads(scope, gopoName); ok { 149 key := gopoName[len(gopoPrefix):] 150 m, tname := checkTypeMethod(scope, key) 151 fns := make([]types.Object, 0, len(names)) 152 for i, name := range names { 153 if name == "" { 154 if m.typ != nil { 155 name = "." 156 } 157 name += m.name + "__" + indexTable[i:i+1] 158 } 159 if obj := lookupFunc(scope, name, tname); obj != nil { 160 fns = append(fns, obj) 161 } 162 } 163 if len(fns) > 0 { 164 newOverload(pkg, scope, m, fns, pos) 165 } 166 delete(overloads, m) 167 } 168 } 169 for key, items := range overloads { 170 off := len(key.name) + 2 171 fns := overloadFuncs(off, items) 172 newOverload(pkg, scope, key, fns, pos) 173 } 174 for name, items := range onameds { 175 off := len(name) + 2 176 nameds := overloadNameds(off, items) 177 if debugImport { 178 log.Println("==> NewOverloadNamed", name) 179 } 180 on := NewOverloadNamed(token.NoPos, pkg, name, nameds...) 181 scope.Insert(on) 182 } 183 } 184 185 // name 186 // .name 187 func lookupFunc(scope *types.Scope, name, tname string) types.Object { 188 if name[0] == '.' { 189 name = name[1:] 190 tobj := scope.Lookup(tname) 191 if tobj != nil { 192 if tn, ok := tobj.(*types.TypeName); ok { 193 if o, ok := tn.Type().(*types.Named); ok { // TODO(xsw): interface support 194 for i, n := 0, o.NumMethods(); i < n; i++ { 195 method := o.Method(i) 196 if method.Name() == name { 197 return method 198 } 199 } 200 } 201 } 202 } 203 } else if o := scope.Lookup(name); o != nil { 204 if _, ok := o.Type().(*types.Signature); ok { 205 return o 206 } 207 } 208 return nil 209 } 210 211 type omthd struct { 212 typ *types.Named 213 name string 214 } 215 216 // Func (no _ func name) 217 // _Func (with _ func name) 218 // TypeName_Method (no _ method name) 219 // _TypeName__Method (with _ method name) 220 func checkTypeMethod(scope *types.Scope, name string) (omthd, string) { 221 if pos := strings.IndexByte(name, '_'); pos >= 0 { 222 nsep := 1 223 if pos == 0 { 224 t := name[1:] 225 if pos = strings.Index(t, "__"); pos <= 0 { 226 return omthd{nil, t}, "" 227 } 228 name, nsep = t, 2 229 } 230 tname, mname := name[:pos], name[pos+nsep:] 231 tobj := scope.Lookup(tname) 232 if tobj != nil { 233 if tn, ok := tobj.(*types.TypeName); ok { 234 if t, ok := tn.Type().(*types.Named); ok { 235 return omthd{t, mname}, tname 236 } 237 } 238 } 239 if tobj != nil || nsep == 2 { 240 log.Panicf("checkTypeMethod: %v not found or not a named type\n", tname) 241 } 242 } 243 return omthd{nil, name}, "" 244 } 245 246 // Gopx_Func 247 // Gopt_TypeName_Method 248 // Gopt__TypeName__Method 249 // Gops_TypeName_Method 250 // Gops__TypeName__Method 251 func checkGoptsx(pkg *types.Package, scope *types.Scope, name string, o types.Object) { 252 const n = len(commonPrefix) 253 const n2 = n + 2 254 if isGopCommon(name) { 255 switch ch := name[n]; ch { 256 case gopsCh, goptCh: // Gops_xxx, Gopt_xxx 257 name = name[n2:] 258 if m, tname := checkTypeMethod(pkg.Scope(), name); m.typ != nil { 259 if ch == goptCh { 260 if debugImport { 261 log.Println("==> NewTemplateRecvMethod", tname, m.name) 262 } 263 NewTemplateRecvMethod(m.typ, token.NoPos, pkg, m.name, o) 264 } else { 265 if debugImport { 266 log.Println("==> NewStaticMethod", tname, m.name) 267 } 268 NewStaticMethod(m.typ, token.NoPos, pkg, m.name, o) 269 } 270 } 271 case gopxCh: // Gopx_xxx 272 aname := name[n2:] 273 o := newFuncEx(token.NoPos, pkg, nil, aname, &tyTypeAsParams{o}) 274 scope.Insert(o) 275 if debugImport { 276 log.Println("==> AliasFunc", name, "=>", aname) 277 } 278 } 279 } 280 } 281 282 const ( 283 commonPrefix = "Gop" 284 285 goptCh = 't' // template method 286 gopsCh = 's' // static method 287 gopxCh = 'x' // type as parameters function/method 288 289 goptPrefix = "Gopt_" // template method 290 gopsPrefix = "Gops_" // static method 291 gopxPrefix = "Gopx_" // type as parameters function/method 292 gopoPrefix = "Gopo_" // overload function/method 293 294 gopPackage = "GopPackage" 295 gopPkgInit = "__gop_inited" 296 ) 297 298 /* 299 const ( 300 Gopo_FuncName = "Func0,Func1,,,Func4" 301 Gopo_TypeName_Method = "Func0,,,,Func4" 302 Gopo__TypeName__Method = "Func0,,,,Func4" 303 ) 304 */ 305 306 func checkOverloads(scope *types.Scope, gopoName string) (ret []string, exists bool) { 307 if o := scope.Lookup(gopoName); o != nil { 308 if c, ok := o.(*types.Const); ok { 309 if v := c.Val(); v.Kind() == constant.String { 310 return strings.Split(constant.StringVal(v), ","), true 311 } 312 } 313 panic("checkOverloads: should be string constant - " + gopoName) 314 } 315 return 316 } 317 318 func newOverload(pkg *types.Package, scope *types.Scope, m omthd, fns []types.Object, pos map[string]token.Pos) { 319 if m.typ == nil { 320 if debugImport { 321 log.Println("==> NewOverloadFunc", m.name) 322 } 323 o := NewOverloadFunc(pos[m.name], pkg, m.name, fns...) 324 scope.Insert(o) 325 checkGoptsx(pkg, scope, m.name, o) 326 } else { 327 if debugImport { 328 log.Println("==> NewOverloadMethod", m.typ.Obj().Name(), m.name) 329 } 330 NewOverloadMethod(m.typ, pos[m.typ.Obj().Name()+"."+m.name], pkg, m.name, fns...) 331 } 332 } 333 334 func overloadFuncs(off int, items []types.Object) []types.Object { 335 fns := make([]types.Object, len(items)) 336 for _, item := range items { 337 idx := toIndex(item.Name()[off]) 338 if idx >= len(items) { 339 log.Panicf("overload func %v out of range 0..%v\n", item.Name(), len(fns)-1) 340 } 341 if fns[idx] != nil { 342 log.Panicf("overload func %v exists?\n", item.Name()) 343 } 344 fns[idx] = item 345 } 346 return fns 347 } 348 349 func overloadNameds(off int, items []*types.Named) []*types.Named { 350 nameds := make([]*types.Named, len(items)) 351 for _, item := range items { 352 name := item.Obj().Name() 353 idx := toIndex(name[off]) 354 if idx >= len(items) { 355 log.Panicf("overload type %v out of range 0..%v\n", name, len(nameds)-1) 356 } 357 if nameds[idx] != nil { 358 log.Panicf("overload type %v exists?\n", name) 359 } 360 nameds[idx] = item 361 } 362 return nameds 363 } 364 365 func toIndex(c byte) int { 366 if c >= '0' && c <= '9' { 367 return int(c - '0') 368 } 369 if c >= 'a' && c <= 'z' { 370 return int(c - ('a' - 10)) 371 } 372 panic("invalid character out of [0-9,a-z]") 373 } 374 375 const ( 376 indexTable = "0123456789abcdefghijklmnopqrstuvwxyz" 377 ) 378 379 // ---------------------------------------------------------------------------- 380 381 type none = struct{} 382 383 type expDeps struct { 384 this *types.Package 385 ret map[*types.Package]none 386 exists map[types.Type]none 387 } 388 389 func checkGopPkg(pkg *Package) (val ast.Expr, ok bool) { 390 if pkg.Types.Name() == "main" || pkg.Types.Scope().Lookup(gopPackage) != nil { 391 return 392 } 393 ed := expDeps{pkg.Types, make(map[*types.Package]none), make(map[types.Type]none)} 394 for _, t := range pkg.expObjTypes { 395 ed.typ(t) 396 } 397 var deps []string 398 for depPkg := range ed.ret { 399 if depPkg.Scope().Lookup(gopPackage) != nil { 400 deps = append(deps, depPkg.Path()) 401 } 402 } 403 if len(deps) > 0 { 404 return stringLit(strings.Join(deps, ",")), true 405 } 406 if ok = pkg.isGopPkg; ok { 407 return identTrue, true 408 } 409 return 410 } 411 412 func (p expDeps) typ(typ types.Type) { 413 retry: 414 switch t := typ.(type) { 415 case *types.Basic: // bool, int, etc 416 case *types.Pointer: 417 typ = t.Elem() 418 goto retry 419 case *types.Slice: 420 typ = t.Elem() 421 goto retry 422 case *types.Map: 423 p.typ(t.Key()) 424 typ = t.Elem() 425 goto retry 426 case *types.Named: 427 p.named(t) 428 case *types.Signature: 429 p.sig(t) 430 case *types.Struct: 431 p.struc(t) 432 case *types.Interface: 433 p.interf(t) 434 case *types.Chan: 435 typ = t.Elem() 436 goto retry 437 case *types.Array: 438 typ = t.Elem() 439 goto retry 440 case *types.TypeParam, *types.Union: 441 default: 442 log.Panicf("expDeps: unknown type - %T\n", typ) 443 } 444 } 445 446 func (p expDeps) sig(sig *types.Signature) { 447 p.tuple(sig.Params()) 448 p.tuple(sig.Results()) 449 } 450 451 func (p expDeps) tuple(v *types.Tuple) { 452 for i, n := 0, v.Len(); i < n; i++ { 453 p.typ(v.At(i).Type()) 454 } 455 } 456 457 func (p expDeps) named(t *types.Named) { 458 o := t.Obj() 459 if at := o.Pkg(); at != nil && at != p.this { 460 if _, ok := p.exists[t]; ok { 461 return 462 } 463 p.exists[t] = none{} 464 p.ret[at] = none{} 465 for i, n := 0, t.NumMethods(); i < n; i++ { 466 m := t.Method(i) 467 p.method(m) 468 } 469 p.typ(t.Underlying()) 470 } 471 } 472 473 func (p expDeps) interf(t *types.Interface) { 474 for i, n := 0, t.NumEmbeddeds(); i < n; i++ { 475 p.typ(t.EmbeddedType(i)) 476 } 477 for i, n := 0, t.NumExplicitMethods(); i < n; i++ { 478 m := t.ExplicitMethod(i) 479 p.method(m) 480 } 481 } 482 483 func (p expDeps) method(m *types.Func) { 484 if m.Exported() { 485 if sig := m.Type().(*types.Signature); !isSigFuncEx(sig) { 486 p.sig(sig) 487 } 488 } 489 } 490 491 func (p expDeps) struc(t *types.Struct) { 492 for i, n := 0, t.NumFields(); i < n; i++ { 493 fld := t.Field(i) 494 if fld.Embedded() || fld.Exported() { 495 p.typ(fld.Type()) 496 } 497 } 498 } 499 500 // initGopPkg initializes a Go+ packages. 501 func (p *Package) initGopPkg(importer types.Importer, pkgImp *types.Package) { 502 scope := pkgImp.Scope() 503 objGopPkg := scope.Lookup(gopPackage) 504 if objGopPkg == nil { // not is a Go+ package 505 return 506 } 507 508 if scope.Lookup(gopPkgInit) != nil { // initialized 509 return 510 } 511 scope.Insert(types.NewConst( 512 token.NoPos, pkgImp, gopPkgInit, types.Typ[types.UntypedBool], constant.MakeBool(true), 513 )) 514 515 pkgDeps, ok := objGopPkg.(*types.Const) 516 if !ok { 517 return 518 } 519 520 var gopDeps []string 521 if v := pkgDeps.Val(); v.Kind() == constant.String { 522 gopDeps = strings.Split(constant.StringVal(v), ",") 523 } 524 525 if debugImport { 526 log.Println("==> Import", pkgImp.Path()) 527 } 528 InitThisGopPkg(pkgImp) 529 for _, depPath := range gopDeps { 530 imp, _ := importer.Import(depPath) 531 p.initGopPkg(importer, imp) 532 } 533 } 534 535 // ---------------------------------------------------------------------------- 536 537 func importPkg(this *Package, pkgPath string, src ast.Node) (PkgRef, error) { 538 if strings.HasPrefix(pkgPath, ".") { // canonical pkgPath 539 pkgPath = path.Join(this.Path(), pkgPath) 540 } 541 pkgImp, err := this.imp.Import(pkgPath) 542 if err != nil { 543 e := &ImportError{Path: pkgPath, Err: err} 544 if src != nil { 545 e.Fset = this.cb.fset 546 e.Pos = src.Pos() 547 } 548 return PkgRef{}, e 549 } else { 550 this.initGopPkg(this.imp, pkgImp) 551 } 552 return PkgRef{Types: pkgImp}, nil 553 } 554 555 // Import imports a package by pkgPath. It will panic if pkgPath not found. 556 func (p *Package) Import(pkgPath string, src ...ast.Node) PkgRef { 557 ret, err := importPkg(p, pkgPath, getSrc(src)) 558 if err != nil { 559 panic(err) 560 } 561 return ret 562 } 563 564 // ForceImport always imports a package (i.e. `import _ pkgPath`). 565 func (p *Package) ForceImport(pkgPath string, src ...ast.Node) { 566 p.Import(pkgPath, src...) 567 p.file.forceImport(pkgPath) 568 } 569 570 // TryImport imports a package by pkgPath. It returns nil if pkgPath not found. 571 func (p *Package) TryImport(pkgPath string) PkgRef { 572 ret, _ := importPkg(p, pkgPath, nil) 573 return ret 574 } 575 576 func (p *Package) big() PkgRef { 577 if p.pkgBig.isNil() { 578 p.pkgBig = p.Import("math/big") 579 } 580 return p.pkgBig 581 } 582 583 // ---------------------------------------------------------------------------- 584 585 type null struct{} 586 type autoNames struct { 587 names map[string]null 588 reqIdx int 589 autoIdx int 590 } 591 592 const ( 593 goxAutoPrefix = "_autoGo_" 594 ) 595 596 func (p *autoNames) initAutoNames() { 597 p.names = make(map[string]null) 598 } 599 600 func (p *autoNames) autoName() string { 601 p.autoIdx++ 602 return goxAutoPrefix + strconv.Itoa(p.autoIdx) 603 } 604 605 func (p *autoNames) useName(name string) { 606 p.names[name] = null{} 607 } 608 609 func (p *autoNames) hasName(name string) bool { 610 _, ok := p.names[name] 611 return ok 612 } 613 614 func (p *autoNames) requireName(name string) (ret string, renamed bool) { 615 ret = name 616 for p.hasName(ret) { 617 p.reqIdx++ 618 ret = name + strconv.Itoa(p.reqIdx) 619 renamed = true 620 } 621 p.useName(ret) 622 return 623 } 624 625 type ImportError struct { 626 Fset dbgPositioner 627 Pos token.Pos 628 Path string 629 Err error 630 } 631 632 func (p *ImportError) Unwrap() error { 633 return p.Err 634 } 635 636 func (p *ImportError) Error() string { 637 if p.Pos == token.NoPos { 638 return fmt.Sprintf("%v", p.Err) 639 } 640 pos := p.Fset.Position(p.Pos) 641 return fmt.Sprintf("%v: %v", pos, p.Err) 642 } 643 644 // ----------------------------------------------------------------------------