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