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  // ----------------------------------------------------------------------------