github.com/goplus/gox@v1.14.13-0.20240308130321-6ff7f61cfae8/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 gox 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) || strings.HasPrefix(name, goptPrefix) || strings.HasPrefix(name, gopxPrefix) 85 } 86 87 func isOverload(name string) bool { 88 n := len(name) 89 return n > 3 && name[n-3:n-1] == "__" 90 } 91 92 // InitThisGopPkg initializes a Go+ package. 93 func InitThisGopPkg(pkg *types.Package) { 94 scope := pkg.Scope() 95 gopos := make([]string, 0, 4) 96 overloads := make(map[omthd][]types.Object) 97 onameds := make(map[string][]*types.Named) 98 names := scope.Names() 99 for _, name := range names { 100 if isGopoConst(name) { 101 gopos = append(gopos, name) 102 continue 103 } 104 o := scope.Lookup(name) 105 if tn, ok := o.(*types.TypeName); ok && tn.IsAlias() { 106 continue 107 } 108 if named, ok := o.Type().(*types.Named); ok { 109 var list methodList 110 switch t := named.Underlying().(type) { 111 case *types.Interface: 112 list = t // add interface overload method to named 113 default: 114 list = named 115 } 116 for i, n := 0, list.NumMethods(); i < n; i++ { 117 m := list.Method(i) 118 mName := m.Name() 119 if isOverload(mName) { // overload method 120 mthd := mName[:len(mName)-3] 121 key := omthd{named, mthd} 122 overloads[key] = append(overloads[key], m) 123 } 124 } 125 if isOverload(name) { // overload named 126 key := name[:len(name)-3] 127 onameds[key] = append(onameds[key], named) 128 } 129 } else if isOverload(name) { // overload function 130 key := omthd{nil, name[:len(name)-3]} 131 overloads[key] = append(overloads[key], o) 132 } else { 133 checkGoptGopx(pkg, scope, name, o) 134 } 135 } 136 for _, gopoName := range gopos { 137 if names, ok := checkOverloads(scope, gopoName); ok { 138 key := gopoName[len(gopoPrefix):] 139 m, tname := checkTypeMethod(scope, key) 140 fns := make([]types.Object, len(names)) 141 for i, name := range names { 142 if name == "" { 143 if m.typ != nil { 144 name = "(" + tname + ")." 145 } 146 name += m.name + "__" + indexTable[i:i+1] 147 } 148 fns[i] = lookupFunc(scope, name, tname) 149 } 150 newOverload(pkg, scope, m, fns) 151 delete(overloads, m) 152 } 153 } 154 for key, items := range overloads { 155 off := len(key.name) + 2 156 fns := overloadFuncs(off, items) 157 newOverload(pkg, scope, key, fns) 158 } 159 for name, items := range onameds { 160 off := len(name) + 2 161 nameds := overloadNameds(off, items) 162 if debugImport { 163 log.Println("==> NewOverloadNamed", name) 164 } 165 on := NewOverloadNamed(token.NoPos, pkg, name, nameds...) 166 scope.Insert(on) 167 } 168 } 169 170 // name 171 // .name 172 // (T).name 173 func lookupFunc(scope *types.Scope, name, tname string) types.Object { 174 first := name[0] 175 if first == '.' || first == '(' { 176 if first == '.' { 177 name = name[1:] 178 } else { 179 next := name[1:] 180 pos := strings.Index(next, ").") 181 if pos <= 0 { 182 log.Panicf("lookupFunc: %v not a valid method, use `(T).method` please\n", name) 183 } 184 tname, name = next[:pos], next[pos+2:] 185 } 186 tobj := scope.Lookup(tname) 187 if tobj != nil { 188 if tn, ok := tobj.(*types.TypeName); ok { 189 if o, ok := tn.Type().(*types.Named); ok { // TODO: interface support 190 for i, n := 0, o.NumMethods(); i < n; i++ { 191 method := o.Method(i) 192 if method.Name() == name { 193 return method 194 } 195 } 196 } 197 } 198 } 199 } else if o := scope.Lookup(name); o != nil { 200 return o 201 } 202 log.Panicf("lookupFunc: %v not found\n", name) 203 return nil 204 } 205 206 type omthd struct { 207 typ *types.Named 208 name string 209 } 210 211 // TypeName_Method 212 // _TypeName__Method 213 func checkTypeMethod(scope *types.Scope, name string) (omthd, string) { 214 if pos := strings.IndexByte(name, '_'); pos >= 0 { 215 nsep := 1 216 if pos == 0 { 217 t := name[1:] 218 if pos = strings.Index(t, "__"); pos <= 0 { 219 return omthd{nil, t}, "" 220 } 221 name, nsep = t, 2 222 } 223 tname, mname := name[:pos], name[pos+nsep:] 224 tobj := scope.Lookup(tname) 225 if tobj != nil { 226 if tn, ok := tobj.(*types.TypeName); ok { 227 if t, ok := tn.Type().(*types.Named); ok { 228 return omthd{t, mname}, tname 229 } 230 } 231 } 232 if tobj != nil || nsep == 2 { 233 log.Panicf("checkTypeMethod: %v not found or not a named type\n", tname) 234 } 235 } 236 return omthd{nil, name}, "" 237 } 238 239 // Gopx_Func 240 // Gopt_TypeName_Method 241 // Gopt__TypeName__Method 242 func checkGoptGopx(pkg *types.Package, scope *types.Scope, name string, o types.Object) { 243 if strings.HasPrefix(name, goptPrefix) { // Gopt_xxx 244 name = name[len(goptPrefix):] 245 if m, tname := checkTypeMethod(pkg.Scope(), name); m.typ != nil { 246 if debugImport { 247 log.Println("==> NewTemplateRecvMethod", tname, m.name) 248 } 249 NewTemplateRecvMethod(m.typ, token.NoPos, pkg, m.name, o) 250 } 251 } else if strings.HasPrefix(name, gopxPrefix) { // Gopx_xxx 252 aname := name[len(gopxPrefix):] 253 o := newFuncEx(token.NoPos, pkg, nil, aname, &tyTypeAsParams{o}) 254 scope.Insert(o) 255 if debugImport { 256 log.Println("==> AliasFunc", name, "=>", aname) 257 } 258 } 259 } 260 261 const ( 262 goptPrefix = "Gopt_" // template method 263 gopoPrefix = "Gopo_" // overload function/method 264 gopxPrefix = "Gopx_" // type as parameters function/method 265 gopPackage = "GopPackage" 266 gopPkgInit = "__gop_inited" 267 ) 268 269 /* 270 const ( 271 Gopo_FuncName = "Func0,Func1,,,Func4" 272 Gopo_TypeName_Method = "Func0,,,,Func4" 273 Gopo__TypeName__Method = "Func0,,,,Func4" 274 ) 275 */ 276 277 func checkOverloads(scope *types.Scope, gopoName string) (ret []string, exists bool) { 278 if o := scope.Lookup(gopoName); o != nil { 279 if c, ok := o.(*types.Const); ok { 280 if v := c.Val(); v.Kind() == constant.String { 281 return strings.Split(constant.StringVal(v), ","), true 282 } 283 } 284 panic("checkOverloads: should be string constant - " + gopoName) 285 } 286 return 287 } 288 289 func newOverload(pkg *types.Package, scope *types.Scope, m omthd, fns []types.Object) { 290 if m.typ == nil { 291 if debugImport { 292 log.Println("==> NewOverloadFunc", m.name) 293 } 294 o := NewOverloadFunc(token.NoPos, pkg, m.name, fns...) 295 scope.Insert(o) 296 checkGoptGopx(pkg, scope, m.name, o) 297 } else { 298 if debugImport { 299 log.Println("==> NewOverloadMethod", m.typ.Obj().Name(), m.name) 300 } 301 NewOverloadMethod(m.typ, token.NoPos, pkg, m.name, fns...) 302 } 303 } 304 305 func overloadFuncs(off int, items []types.Object) []types.Object { 306 fns := make([]types.Object, len(items)) 307 for _, item := range items { 308 idx := toIndex(item.Name()[off]) 309 if idx >= len(items) { 310 log.Panicf("overload func %v out of range 0..%v\n", item.Name(), len(fns)-1) 311 } 312 if fns[idx] != nil { 313 log.Panicf("overload func %v exists?\n", item.Name()) 314 } 315 fns[idx] = item 316 } 317 return fns 318 } 319 320 func overloadNameds(off int, items []*types.Named) []*types.Named { 321 nameds := make([]*types.Named, len(items)) 322 for _, item := range items { 323 name := item.Obj().Name() 324 idx := toIndex(name[off]) 325 if idx >= len(items) { 326 log.Panicf("overload type %v out of range 0..%v\n", name, len(nameds)-1) 327 } 328 if nameds[idx] != nil { 329 log.Panicf("overload type %v exists?\n", name) 330 } 331 nameds[idx] = item 332 } 333 return nameds 334 } 335 336 func toIndex(c byte) int { 337 if c >= '0' && c <= '9' { 338 return int(c - '0') 339 } 340 if c >= 'a' && c <= 'z' { 341 return int(c - ('a' - 10)) 342 } 343 panic("invalid character out of [0-9,a-z]") 344 } 345 346 const ( 347 indexTable = "0123456789abcdefghijklmnopqrstuvwxyz" 348 ) 349 350 // ---------------------------------------------------------------------------- 351 352 type expDeps struct { 353 this *types.Package 354 ret map[*types.Package]none 355 exists map[types.Type]none 356 } 357 358 func checkGopPkg(pkg *Package) (val ast.Expr, ok bool) { 359 if pkg.Types.Name() == "main" || pkg.Types.Scope().Lookup(gopPackage) != nil { 360 return 361 } 362 ed := expDeps{pkg.Types, make(map[*types.Package]none), make(map[types.Type]none)} 363 for _, t := range pkg.expObjTypes { 364 ed.typ(t) 365 } 366 var deps []string 367 for depPkg := range ed.ret { 368 if depPkg.Scope().Lookup(gopPackage) != nil { 369 deps = append(deps, depPkg.Path()) 370 } 371 } 372 if len(deps) > 0 { 373 return stringLit(strings.Join(deps, ",")), true 374 } 375 if ok = pkg.isGopPkg; ok { 376 return identTrue, true 377 } 378 return 379 } 380 381 func (p expDeps) typ(typ types.Type) { 382 retry: 383 switch t := typ.(type) { 384 case *types.Basic: // bool, int, etc 385 case *types.Pointer: 386 typ = t.Elem() 387 goto retry 388 case *types.Slice: 389 typ = t.Elem() 390 goto retry 391 case *types.Map: 392 p.typ(t.Key()) 393 typ = t.Elem() 394 goto retry 395 case *types.Named: 396 p.named(t) 397 case *types.Signature: 398 p.sig(t) 399 case *types.Struct: 400 p.struc(t) 401 case *types.Interface: 402 p.interf(t) 403 case *types.Chan: 404 typ = t.Elem() 405 goto retry 406 case *types.Array: 407 typ = t.Elem() 408 goto retry 409 case *types.TypeParam, *types.Union: 410 default: 411 log.Panicf("expDeps: unknown type - %T\n", typ) 412 } 413 } 414 415 func (p expDeps) sig(sig *types.Signature) { 416 p.tuple(sig.Params()) 417 p.tuple(sig.Results()) 418 } 419 420 func (p expDeps) tuple(v *types.Tuple) { 421 for i, n := 0, v.Len(); i < n; i++ { 422 p.typ(v.At(i).Type()) 423 } 424 } 425 426 func (p expDeps) named(t *types.Named) { 427 o := t.Obj() 428 if at := o.Pkg(); at != nil && at != p.this { 429 if _, ok := p.exists[t]; ok { 430 return 431 } 432 p.exists[t] = none{} 433 p.ret[at] = none{} 434 for i, n := 0, t.NumMethods(); i < n; i++ { 435 m := t.Method(i) 436 p.method(m) 437 } 438 p.typ(t.Underlying()) 439 } 440 } 441 442 func (p expDeps) interf(t *types.Interface) { 443 for i, n := 0, t.NumEmbeddeds(); i < n; i++ { 444 p.typ(t.EmbeddedType(i)) 445 } 446 for i, n := 0, t.NumExplicitMethods(); i < n; i++ { 447 m := t.ExplicitMethod(i) 448 p.method(m) 449 } 450 } 451 452 func (p expDeps) method(m *types.Func) { 453 if m.Exported() { 454 if sig := m.Type().(*types.Signature); !isSigFuncEx(sig) { 455 p.sig(sig) 456 } 457 } 458 } 459 460 func (p expDeps) struc(t *types.Struct) { 461 for i, n := 0, t.NumFields(); i < n; i++ { 462 fld := t.Field(i) 463 if fld.Embedded() || fld.Exported() { 464 p.typ(fld.Type()) 465 } 466 } 467 } 468 469 // initGopPkg initializes a Go+ packages. 470 func (p *Package) initGopPkg(importer types.Importer, pkgImp *types.Package) { 471 scope := pkgImp.Scope() 472 objGopPkg := scope.Lookup(gopPackage) 473 if objGopPkg == nil { // not is a Go+ package 474 return 475 } 476 477 if scope.Lookup(gopPkgInit) != nil { // initialized 478 return 479 } 480 scope.Insert(types.NewConst( 481 token.NoPos, pkgImp, gopPkgInit, types.Typ[types.UntypedBool], constant.MakeBool(true), 482 )) 483 484 pkgDeps, ok := objGopPkg.(*types.Const) 485 if !ok { 486 return 487 } 488 489 var gopDeps []string 490 if v := pkgDeps.Val(); v.Kind() == constant.String { 491 gopDeps = strings.Split(constant.StringVal(v), ",") 492 } 493 494 if debugImport { 495 log.Println("==> Import", pkgImp.Path()) 496 } 497 InitThisGopPkg(pkgImp) 498 for _, depPath := range gopDeps { 499 imp, _ := importer.Import(depPath) 500 p.initGopPkg(importer, imp) 501 } 502 } 503 504 // ---------------------------------------------------------------------------- 505 506 func importPkg(this *Package, pkgPath string, src ast.Node) (PkgRef, error) { 507 if strings.HasPrefix(pkgPath, ".") { // canonical pkgPath 508 pkgPath = path.Join(this.Path(), pkgPath) 509 } 510 pkgImp, err := this.imp.Import(pkgPath) 511 if err != nil { 512 e := &ImportError{Path: pkgPath, Err: err} 513 if src != nil { 514 e.Fset = this.cb.fset 515 e.Pos = src.Pos() 516 } 517 return PkgRef{}, e 518 } else { 519 this.initGopPkg(this.imp, pkgImp) 520 } 521 return PkgRef{Types: pkgImp}, nil 522 } 523 524 // Import imports a package by pkgPath. It will panic if pkgPath not found. 525 func (p *Package) Import(pkgPath string, src ...ast.Node) PkgRef { 526 ret, err := importPkg(p, pkgPath, getSrc(src)) 527 if err != nil { 528 panic(err) 529 } 530 return ret 531 } 532 533 // ForceImport always imports a package (i.e. `import _ pkgPath`). 534 func (p *Package) ForceImport(pkgPath string, src ...ast.Node) { 535 p.Import(pkgPath, src...) 536 p.file.forceImport(pkgPath) 537 } 538 539 // TryImport imports a package by pkgPath. It returns nil if pkgPath not found. 540 func (p *Package) TryImport(pkgPath string) PkgRef { 541 ret, _ := importPkg(p, pkgPath, nil) 542 return ret 543 } 544 545 func (p *Package) big() PkgRef { 546 if p.pkgBig.isNil() { 547 p.pkgBig = p.Import("math/big") 548 } 549 return p.pkgBig 550 } 551 552 // ---------------------------------------------------------------------------- 553 554 type null struct{} 555 type autoNames struct { 556 names map[string]null 557 reqIdx int 558 autoIdx int 559 } 560 561 const ( 562 goxAutoPrefix = "_autoGo_" 563 ) 564 565 func (p *autoNames) initAutoNames() { 566 p.names = make(map[string]null) 567 } 568 569 func (p *autoNames) autoName() string { 570 p.autoIdx++ 571 return goxAutoPrefix + strconv.Itoa(p.autoIdx) 572 } 573 574 func (p *autoNames) useName(name string) { 575 p.names[name] = null{} 576 } 577 578 func (p *autoNames) hasName(name string) bool { 579 _, ok := p.names[name] 580 return ok 581 } 582 583 func (p *autoNames) requireName(name string) (ret string, renamed bool) { 584 ret = name 585 for p.hasName(ret) { 586 p.reqIdx++ 587 ret = name + strconv.Itoa(p.reqIdx) 588 renamed = true 589 } 590 p.useName(ret) 591 return 592 } 593 594 type ImportError struct { 595 Fset dbgPositioner 596 Pos token.Pos 597 Path string 598 Err error 599 } 600 601 func (p *ImportError) Unwrap() error { 602 return p.Err 603 } 604 605 func (p *ImportError) Error() string { 606 if p.Pos == token.NoPos { 607 return fmt.Sprintf("%v", p.Err) 608 } 609 pos := p.Fset.Position(p.Pos) 610 return fmt.Sprintf("%v: %v", pos, p.Err) 611 } 612 613 // ----------------------------------------------------------------------------