github.com/goplus/igop@v0.17.0/context.go (about)

     1  /*
     2   * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package igop
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"flag"
    23  	"fmt"
    24  	"go/ast"
    25  	"go/build"
    26  	"go/parser"
    27  	"go/token"
    28  	"go/types"
    29  	"io"
    30  	"log"
    31  	"os"
    32  	"path/filepath"
    33  	"reflect"
    34  	"runtime"
    35  	"sort"
    36  	"strconv"
    37  	"strings"
    38  	"sync"
    39  	"time"
    40  
    41  	"github.com/goplus/igop/load"
    42  	"golang.org/x/tools/go/ssa"
    43  )
    44  
    45  // Mode is a bitmask of options affecting the interpreter.
    46  type Mode uint
    47  
    48  const (
    49  	DisableRecover        Mode = 1 << iota // Disable recover() in target programs; show interpreter crash instead.
    50  	DisableCustomBuiltin                   // Disable load custom builtin func
    51  	EnableDumpImports                      // print import packages
    52  	EnableDumpInstr                        // Print packages & SSA instruction code
    53  	EnableTracing                          // Print a trace of all instructions as they are interpreted.
    54  	EnablePrintAny                         // Enable builtin print for any type ( struct/array )
    55  	EnableNoStrict                         // Enable no strict mode
    56  	ExperimentalSupportGC                  // experimental support runtime.GC
    57  	SupportMultipleInterp                  // Support multiple interp, must manual release interp reflectx icall.
    58  )
    59  
    60  // Loader types loader interface
    61  type Loader interface {
    62  	Import(path string) (*types.Package, error)
    63  	Installed(path string) (*Package, bool)
    64  	Packages() []*types.Package
    65  	LookupReflect(typ types.Type) (reflect.Type, bool)
    66  	LookupTypes(typ reflect.Type) (types.Type, bool)
    67  	SetImport(path string, pkg *types.Package, load func() error) error
    68  }
    69  
    70  // Context ssa context
    71  type Context struct {
    72  	Loader       Loader                                                   // types loader
    73  	BuildContext build.Context                                            // build context, default build.Default
    74  	RunContext   context.Context                                          // run context, default unset
    75  	output       io.Writer                                                // capture print/println output
    76  	FileSet      *token.FileSet                                           // file set
    77  	sizes        types.Sizes                                              // types unsafe sizes
    78  	Lookup       func(root, path string) (dir string, found bool)         // lookup external import
    79  	evalCallFn   func(interp *Interp, call *ssa.Call, res ...interface{}) // internal eval func for repl
    80  	debugFunc    func(*DebugInfo)                                         // debug func
    81  	pkgs         map[string]*sourcePackage                                // imports
    82  	override     map[string]reflect.Value                                 // override function
    83  	evalInit     map[string]bool                                          // eval init check
    84  	nestedMap    map[*types.Named]int                                     // nested named index
    85  	root         string                                                   // project root
    86  	callForPool  int                                                      // least call count for enable function pool
    87  	Mode         Mode                                                     // mode
    88  	BuilderMode  ssa.BuilderMode                                          // ssa builder mode
    89  	evalMode     bool                                                     // eval mode
    90  }
    91  
    92  func (ctx *Context) setRoot(root string) {
    93  	ctx.root = root
    94  }
    95  
    96  func (ctx *Context) lookupPath(path string) (dir string, found bool) {
    97  	if ctx.Lookup != nil {
    98  		dir, found = ctx.Lookup(ctx.root, path)
    99  	}
   100  	if !found {
   101  		bp, err := build.Import(path, ctx.root, build.FindOnly)
   102  		if err == nil && bp.ImportPath == path {
   103  			return bp.Dir, true
   104  		}
   105  	}
   106  	return
   107  }
   108  
   109  type sourcePackage struct {
   110  	Context *Context
   111  	Package *types.Package
   112  	Info    *types.Info
   113  	Dir     string
   114  	Files   []*ast.File
   115  	Links   []*load.LinkSym
   116  }
   117  
   118  func (sp *sourcePackage) Load() (err error) {
   119  	if sp.Info == nil {
   120  		sp.Info = newTypesInfo()
   121  		conf := &types.Config{
   122  			Sizes:    sp.Context.sizes,
   123  			Importer: NewImporter(sp.Context),
   124  		}
   125  		if sp.Context.evalMode {
   126  			conf.DisableUnusedImportCheck = true
   127  		}
   128  		if sp.Context.Mode&EnableNoStrict != 0 {
   129  			conf.Error = func(e error) {
   130  				if te, ok := e.(types.Error); ok {
   131  					if strings.HasSuffix(te.Msg, errDeclaredNotUsed) || strings.HasSuffix(te.Msg, errImportedNotUsed) {
   132  						println(fmt.Sprintf("igop warning: %v", e))
   133  						return
   134  					}
   135  				}
   136  				if err == nil {
   137  					err = e
   138  				}
   139  			}
   140  		} else {
   141  			conf.Error = func(e error) {
   142  				if err == nil {
   143  					err = e
   144  				}
   145  			}
   146  		}
   147  		types.NewChecker(conf, sp.Context.FileSet, sp.Package, sp.Info).Files(sp.Files)
   148  		if err == nil {
   149  			sp.Links, err = load.ParseLinkname(sp.Context.FileSet, sp.Package.Path(), sp.Files)
   150  		}
   151  	}
   152  	return
   153  }
   154  
   155  // NewContext create a new Context
   156  func NewContext(mode Mode) *Context {
   157  	ctx := &Context{
   158  		FileSet:      token.NewFileSet(),
   159  		Mode:         mode,
   160  		BuilderMode:  0, //ssa.SanityCheckFunctions,
   161  		BuildContext: build.Default,
   162  		pkgs:         make(map[string]*sourcePackage),
   163  		override:     make(map[string]reflect.Value),
   164  		nestedMap:    make(map[*types.Named]int),
   165  		callForPool:  64,
   166  	}
   167  	ctx.Loader = NewTypesLoader(ctx, mode)
   168  	if mode&EnableDumpInstr != 0 {
   169  		ctx.BuilderMode |= ssa.PrintFunctions
   170  	}
   171  	if mode&ExperimentalSupportGC != 0 {
   172  		ctx.RegisterExternal("runtime.GC", runtimeGC)
   173  	}
   174  	ctx.sizes = types.SizesFor("gc", runtime.GOARCH)
   175  	ctx.Lookup = new(load.ListDriver).Lookup
   176  
   177  	return ctx
   178  }
   179  
   180  func (ctx *Context) UnsafeRelease() {
   181  	ctx.pkgs = nil
   182  	ctx.Loader = nil
   183  	ctx.override = nil
   184  }
   185  
   186  func (ctx *Context) IsEvalMode() bool {
   187  	return ctx.evalMode
   188  }
   189  
   190  func (ctx *Context) SetEvalMode(b bool) {
   191  	ctx.evalMode = b
   192  }
   193  
   194  // SetUnsafeSizes set the sizing functions for package unsafe.
   195  func (ctx *Context) SetUnsafeSizes(sizes types.Sizes) {
   196  	ctx.sizes = sizes
   197  }
   198  
   199  // SetLeastCallForEnablePool set least call count for enable function pool, default 64
   200  func (ctx *Context) SetLeastCallForEnablePool(count int) {
   201  	ctx.callForPool = count
   202  }
   203  
   204  func (ctx *Context) SetDebug(fn func(*DebugInfo)) {
   205  	ctx.BuilderMode |= ssa.GlobalDebug
   206  	ctx.debugFunc = fn
   207  }
   208  
   209  // RegisterExternal register external value must variable address or func.
   210  func (ctx *Context) RegisterExternal(key string, i interface{}) {
   211  	if i == nil {
   212  		delete(ctx.override, key)
   213  		return
   214  	}
   215  	v := reflect.ValueOf(i)
   216  	switch v.Kind() {
   217  	case reflect.Func, reflect.Ptr:
   218  		ctx.override[key] = v
   219  	default:
   220  		log.Printf("register external must variable address or func. not %v\n", v.Kind())
   221  	}
   222  }
   223  
   224  // SetPrintOutput is captured builtin print/println output
   225  func (ctx *Context) SetPrintOutput(output *bytes.Buffer) {
   226  	ctx.output = output
   227  }
   228  
   229  func (ctx *Context) writeOutput(data []byte) (n int, err error) {
   230  	if ctx.output != nil {
   231  		return ctx.output.Write(data)
   232  	}
   233  	return os.Stdout.Write(data)
   234  }
   235  
   236  func (ctx *Context) LoadDir(dir string, test bool) (pkg *ssa.Package, err error) {
   237  	bp, err := ctx.BuildContext.ImportDir(dir, 0)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  	importPath, err := load.GetImportPath(bp.Name, dir)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	bp.ImportPath = importPath
   246  	var sp *sourcePackage
   247  	if test {
   248  		sp, err = ctx.loadTestPackage(bp, importPath, dir)
   249  	} else {
   250  		sp, err = ctx.loadPackage(bp, importPath, dir)
   251  	}
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	if ctx.Mode&DisableCustomBuiltin == 0 {
   256  		if f, err := ParseBuiltin(ctx.FileSet, sp.Package.Name()); err == nil {
   257  			sp.Files = append([]*ast.File{f}, sp.Files...)
   258  		}
   259  	}
   260  	ctx.setRoot(dir)
   261  	if dir != "." {
   262  		if wd, err := os.Getwd(); err == nil {
   263  			os.Chdir(dir)
   264  			defer os.Chdir(wd)
   265  		}
   266  	}
   267  	err = sp.Load()
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  	return ctx.buildPackage(sp)
   272  }
   273  
   274  func RegisterFileProcess(ext string, fn SourceProcessFunc) {
   275  	sourceProcessor[ext] = fn
   276  }
   277  
   278  type SourceProcessFunc func(ctx *Context, filename string, src interface{}) ([]byte, error)
   279  
   280  var (
   281  	sourceProcessor = make(map[string]SourceProcessFunc)
   282  )
   283  
   284  func (ctx *Context) AddImportFile(path string, filename string, src interface{}) (err error) {
   285  	_, err = ctx.addImportFile(path, filename, src)
   286  	return
   287  }
   288  
   289  func (ctx *Context) AddImport(path string, dir string) (err error) {
   290  	_, err = ctx.addImport(path, dir)
   291  	return
   292  }
   293  
   294  func (ctx *Context) addImportFile(path string, filename string, src interface{}) (*sourcePackage, error) {
   295  	tp, err := ctx.loadPackageFile(path, filename, src)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  	ctx.Loader.SetImport(path, tp.Package, tp.Load)
   300  	return tp, nil
   301  }
   302  
   303  func (ctx *Context) addImport(path string, dir string) (*sourcePackage, error) {
   304  	bp, err := ctx.BuildContext.ImportDir(dir, 0)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  	bp.ImportPath = path
   309  	tp, err := ctx.loadPackage(bp, path, dir)
   310  	if err != nil {
   311  		return nil, err
   312  	}
   313  	ctx.Loader.SetImport(path, tp.Package, tp.Load)
   314  	return tp, nil
   315  }
   316  
   317  func (ctx *Context) loadPackageFile(path string, filename string, src interface{}) (*sourcePackage, error) {
   318  	file, err := ctx.ParseFile(filename, src)
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  	pkg := types.NewPackage(path, file.Name.Name)
   323  	tp := &sourcePackage{
   324  		Context: ctx,
   325  		Package: pkg,
   326  		Files:   []*ast.File{file},
   327  	}
   328  	ctx.pkgs[path] = tp
   329  	return tp, nil
   330  }
   331  
   332  func (ctx *Context) loadPackage(bp *build.Package, path string, dir string) (*sourcePackage, error) {
   333  	files, err := ctx.parseGoFiles(dir, append(bp.GoFiles, bp.CgoFiles...))
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	embed, err := load.Embed(bp, ctx.FileSet, files, false, false)
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	if embed != nil {
   342  		files = append(files, embed)
   343  	}
   344  	tp := &sourcePackage{
   345  		Package: types.NewPackage(path, bp.Name),
   346  		Files:   files,
   347  		Dir:     dir,
   348  		Context: ctx,
   349  	}
   350  	ctx.pkgs[path] = tp
   351  	return tp, nil
   352  }
   353  
   354  func (ctx *Context) loadTestPackage(bp *build.Package, path string, dir string) (*sourcePackage, error) {
   355  	if len(bp.TestGoFiles) == 0 && len(bp.XTestGoFiles) == 0 {
   356  		return nil, ErrNoTestFiles
   357  	}
   358  	files, err := ctx.parseGoFiles(dir, append(append(bp.GoFiles, bp.CgoFiles...), bp.TestGoFiles...))
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  	embed, err := load.Embed(bp, ctx.FileSet, files, true, false)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  	if embed != nil {
   367  		files = append(files, embed)
   368  	}
   369  	tp := &sourcePackage{
   370  		Package: types.NewPackage(path, bp.Name),
   371  		Files:   files,
   372  		Dir:     dir,
   373  		Context: ctx,
   374  	}
   375  	ctx.pkgs[path] = tp
   376  	if len(bp.XTestGoFiles) > 0 {
   377  		files, err := ctx.parseGoFiles(dir, bp.XTestGoFiles)
   378  		if err != nil {
   379  			return nil, err
   380  		}
   381  		embed, err := load.Embed(bp, ctx.FileSet, files, false, true)
   382  		if err != nil {
   383  			return nil, err
   384  		}
   385  		if embed != nil {
   386  			files = append(files, embed)
   387  		}
   388  		tp := &sourcePackage{
   389  			Package: types.NewPackage(path+"_test", bp.Name+"_test"),
   390  			Files:   files,
   391  			Dir:     dir,
   392  			Context: ctx,
   393  		}
   394  		ctx.pkgs[path+"_test"] = tp
   395  	}
   396  	data, err := load.TestMain(bp)
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  	f, err := parser.ParseFile(ctx.FileSet, "_testmain.go", data, parser.ParseComments)
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  	return &sourcePackage{
   405  		Package: types.NewPackage(path+".test", "main"),
   406  		Files:   []*ast.File{f},
   407  		Dir:     dir,
   408  		Context: ctx,
   409  	}, nil
   410  }
   411  
   412  func (ctx *Context) parseGoFiles(dir string, filenames []string) ([]*ast.File, error) {
   413  	files := make([]*ast.File, len(filenames))
   414  	errors := make([]error, len(filenames))
   415  
   416  	var wg sync.WaitGroup
   417  	wg.Add(len(filenames))
   418  	for i, filename := range filenames {
   419  		go func(i int, filepath string) {
   420  			defer wg.Done()
   421  			files[i], errors[i] = parser.ParseFile(ctx.FileSet, filepath, nil, parser.ParseComments)
   422  		}(i, filepath.Join(dir, filename))
   423  	}
   424  	wg.Wait()
   425  
   426  	for _, err := range errors {
   427  		if err != nil {
   428  			return nil, err
   429  		}
   430  	}
   431  	return files, nil
   432  }
   433  
   434  func (ctx *Context) LoadInterp(filename string, src interface{}) (*Interp, error) {
   435  	pkg, err := ctx.LoadFile(filename, src)
   436  	if err != nil {
   437  		return nil, err
   438  	}
   439  	return ctx.NewInterp(pkg)
   440  }
   441  
   442  func (ctx *Context) LoadFile(filename string, src interface{}) (*ssa.Package, error) {
   443  	file, err := ctx.ParseFile(filename, src)
   444  	if err != nil {
   445  		return nil, err
   446  	}
   447  	root, _ := filepath.Split(filename)
   448  	ctx.setRoot(root)
   449  	return ctx.LoadAstFile(file.Name.Name, file)
   450  }
   451  
   452  func (ctx *Context) ParseFile(filename string, src interface{}) (*ast.File, error) {
   453  	if ext := filepath.Ext(filename); ext != "" {
   454  		if fn, ok := sourceProcessor[ext]; ok {
   455  			data, err := fn(ctx, filename, src)
   456  			if err != nil {
   457  				return nil, err
   458  			}
   459  			src = data
   460  		}
   461  	}
   462  	return parser.ParseFile(ctx.FileSet, filename, src, parser.ParseComments)
   463  }
   464  
   465  func (ctx *Context) LoadAstFile(path string, file *ast.File) (*ssa.Package, error) {
   466  	files := []*ast.File{file}
   467  	if ctx.Mode&DisableCustomBuiltin == 0 {
   468  		if f, err := ParseBuiltin(ctx.FileSet, file.Name.Name); err == nil {
   469  			files = []*ast.File{f, file}
   470  		}
   471  	}
   472  	dir, _ := filepath.Split(ctx.FileSet.Position(file.Package).Filename)
   473  	if dir == "" {
   474  		dir, _ = os.Getwd()
   475  	}
   476  	embed, err := load.EmbedFiles(file.Name.Name, filepath.Clean(dir), ctx.FileSet, files)
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  	if embed != nil {
   481  		files = append(files, embed)
   482  	}
   483  	sp := &sourcePackage{
   484  		Context: ctx,
   485  		Package: types.NewPackage(path, file.Name.Name),
   486  		Files:   files,
   487  	}
   488  	if err := sp.Load(); err != nil {
   489  		return nil, err
   490  	}
   491  	ctx.pkgs[path] = sp
   492  	return ctx.buildPackage(sp)
   493  }
   494  
   495  func (ctx *Context) LoadAstPackage(path string, apkg *ast.Package) (*ssa.Package, error) {
   496  	var files []*ast.File
   497  	for _, f := range apkg.Files {
   498  		files = append(files, f)
   499  	}
   500  	if ctx.Mode&DisableCustomBuiltin == 0 {
   501  		if f, err := ParseBuiltin(ctx.FileSet, apkg.Name); err == nil {
   502  			files = append([]*ast.File{f}, files...)
   503  		}
   504  	}
   505  	sp := &sourcePackage{
   506  		Context: ctx,
   507  		Package: types.NewPackage(path, apkg.Name),
   508  		Files:   files,
   509  	}
   510  	err := sp.Load()
   511  	if err != nil {
   512  		return nil, err
   513  	}
   514  	return ctx.buildPackage(sp)
   515  }
   516  
   517  func (ctx *Context) RunPkg(mainPkg *ssa.Package, input string, args []string) (exitCode int, err error) {
   518  	interp, err := ctx.NewInterp(mainPkg)
   519  	if err != nil {
   520  		return 2, err
   521  	}
   522  	return ctx.RunInterp(interp, input, args)
   523  }
   524  
   525  func (ctx *Context) RunInterp(interp *Interp, input string, args []string) (exitCode int, err error) {
   526  	if ctx.RunContext != nil {
   527  		return ctx.runInterpWithContext(interp, input, args, ctx.RunContext)
   528  	}
   529  	return ctx.runInterp(interp, input, args)
   530  }
   531  
   532  func (p *Context) runInterpWithContext(interp *Interp, input string, args []string, ctx context.Context) (int, error) {
   533  	var exitCode int
   534  	var err error
   535  	ch := make(chan error, 1)
   536  	interp.cherror = make(chan PanicError)
   537  	go func() {
   538  		exitCode, err = p.runInterp(interp, input, args)
   539  		ch <- err
   540  	}()
   541  	select {
   542  	case <-ctx.Done():
   543  		interp.Abort()
   544  		select {
   545  		case <-time.After(1e9):
   546  			exitCode = 2
   547  			err = fmt.Errorf("interrupt timeout: all goroutines are asleep - deadlock!")
   548  		case <-ch:
   549  			err = ctx.Err()
   550  		}
   551  	case err = <-ch:
   552  	case e := <-interp.cherror:
   553  		switch v := e.Value.(type) {
   554  		case exitPanic:
   555  			exitCode = int(v)
   556  		default:
   557  			err = e
   558  			interp.Abort()
   559  			exitCode = 2
   560  		}
   561  	}
   562  	return exitCode, err
   563  }
   564  
   565  func (ctx *Context) runInterp(interp *Interp, input string, args []string) (exitCode int, err error) {
   566  	// reset os args and flag
   567  	os.Args = []string{input}
   568  	if args != nil {
   569  		os.Args = append(os.Args, args...)
   570  	}
   571  	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
   572  	if err = interp.RunInit(); err != nil {
   573  		return 2, err
   574  	}
   575  	return interp.RunMain()
   576  }
   577  
   578  func (ctx *Context) RunFunc(mainPkg *ssa.Package, fnname string, args ...Value) (ret Value, err error) {
   579  	interp, err := ctx.NewInterp(mainPkg)
   580  	if err != nil {
   581  		return nil, err
   582  	}
   583  	return interp.RunFunc(fnname, args...)
   584  }
   585  
   586  func (ctx *Context) NewInterp(mainPkg *ssa.Package) (*Interp, error) {
   587  	return NewInterp(ctx, mainPkg)
   588  }
   589  
   590  func (ctx *Context) TestPkg(pkg *ssa.Package, input string, args []string) error {
   591  	var failed bool
   592  	start := time.Now()
   593  	defer func() {
   594  		sec := time.Since(start).Seconds()
   595  		if failed {
   596  			fmt.Fprintf(os.Stdout, "FAIL\t%s %0.3fs\n", pkg.Pkg.Path(), sec)
   597  		} else {
   598  			fmt.Fprintf(os.Stdout, "ok\t%s %0.3fs\n", pkg.Pkg.Path(), sec)
   599  		}
   600  	}()
   601  	os.Args = []string{input}
   602  	if args != nil {
   603  		os.Args = append(os.Args, args...)
   604  	}
   605  	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
   606  	interp, err := NewInterp(ctx, pkg)
   607  	if err != nil {
   608  		failed = true
   609  		fmt.Printf("create interp failed: %v\n", err)
   610  	}
   611  	if err = interp.RunInit(); err != nil {
   612  		failed = true
   613  		fmt.Printf("init error: %v\n", err)
   614  	}
   615  	exitCode, _ := interp.RunMain()
   616  	if exitCode != 0 {
   617  		failed = true
   618  	}
   619  	if failed {
   620  		return ErrTestFailed
   621  	}
   622  	return nil
   623  }
   624  
   625  func (ctx *Context) RunFile(filename string, src interface{}, args []string) (exitCode int, err error) {
   626  	pkg, err := ctx.LoadFile(filename, src)
   627  	if err != nil {
   628  		return 2, err
   629  	}
   630  	return ctx.RunPkg(pkg, filename, args)
   631  }
   632  
   633  func (ctx *Context) Run(path string, args []string) (exitCode int, err error) {
   634  	if strings.HasSuffix(path, ".go") {
   635  		return ctx.RunFile(path, nil, args)
   636  	}
   637  	pkg, err := ctx.LoadDir(path, false)
   638  	if err != nil {
   639  		return 2, err
   640  	}
   641  	if !isMainPkg(pkg) {
   642  		return 2, ErrNotFoundMain
   643  	}
   644  	return ctx.RunPkg(pkg, path, args)
   645  }
   646  
   647  func isMainPkg(pkg *ssa.Package) bool {
   648  	return pkg.Pkg.Name() == "main" && pkg.Func("main") != nil
   649  }
   650  
   651  func (ctx *Context) RunTest(dir string, args []string) error {
   652  	pkg, err := ctx.LoadDir(dir, true)
   653  	if err != nil {
   654  		if err == ErrNoTestFiles {
   655  			fmt.Println("?", err)
   656  			return nil
   657  		}
   658  		return err
   659  	}
   660  	if filepath.IsAbs(dir) {
   661  		os.Chdir(dir)
   662  	}
   663  	return ctx.TestPkg(pkg, dir, args)
   664  }
   665  
   666  func (ctx *Context) buildPackage(sp *sourcePackage) (pkg *ssa.Package, err error) {
   667  	if ctx.Mode&DisableRecover == 0 {
   668  		defer func() {
   669  			if e := recover(); e != nil {
   670  				err = fmt.Errorf("build SSA package error: %v", e)
   671  			}
   672  		}()
   673  	}
   674  	mode := ctx.BuilderMode
   675  	if enabledTypeParam {
   676  		mode |= ssa.InstantiateGenerics
   677  	}
   678  	prog := ssa.NewProgram(ctx.FileSet, mode)
   679  	// Create SSA packages for all imports.
   680  	// Order is not significant.
   681  	created := make(map[*types.Package]bool)
   682  	var createAll func(pkgs []*types.Package)
   683  	createAll = func(pkgs []*types.Package) {
   684  		for _, p := range pkgs {
   685  			if !created[p] {
   686  				created[p] = true
   687  				createAll(p.Imports())
   688  				if pkg, ok := ctx.pkgs[p.Path()]; ok {
   689  					if ctx.Mode&EnableDumpImports != 0 {
   690  						if pkg.Dir != "" {
   691  							fmt.Println("# package", p.Path(), pkg.Dir)
   692  						} else {
   693  							fmt.Println("# package", p.Path(), "<memory>")
   694  						}
   695  					}
   696  					prog.CreatePackage(p, pkg.Files, pkg.Info, true).Build()
   697  					ctx.checkNested(pkg.Package, pkg.Info)
   698  				} else {
   699  					var indirect bool
   700  					if !p.Complete() {
   701  						indirect = true
   702  						p.MarkComplete()
   703  					}
   704  					if ctx.Mode&EnableDumpImports != 0 {
   705  						if indirect {
   706  							fmt.Println("# virtual", p.Path())
   707  						} else {
   708  							fmt.Println("# builtin", p.Path())
   709  						}
   710  					}
   711  					prog.CreatePackage(p, nil, nil, true).Build()
   712  				}
   713  			}
   714  		}
   715  	}
   716  	var addin []*types.Package
   717  	for _, pkg := range ctx.Loader.Packages() {
   718  		if !pkg.Complete() {
   719  			addin = append(addin, pkg)
   720  		}
   721  	}
   722  	if len(addin) > 0 {
   723  		sort.Slice(addin, func(i, j int) bool {
   724  			return addin[i].Path() < addin[j].Path()
   725  		})
   726  		createAll(addin)
   727  	}
   728  	createAll(sp.Package.Imports())
   729  	if ctx.Mode&EnableDumpImports != 0 {
   730  		if sp.Dir != "" {
   731  			fmt.Println("# package", sp.Package.Path(), sp.Dir)
   732  		} else {
   733  			fmt.Println("# package", sp.Package.Path(), "<source>")
   734  		}
   735  	}
   736  	// Create and build the primary package.
   737  	pkg = prog.CreatePackage(sp.Package, sp.Files, sp.Info, false)
   738  	pkg.Build()
   739  	ctx.checkNested(sp.Package, sp.Info)
   740  	return
   741  }
   742  
   743  func (ctx *Context) checkNested(pkg *types.Package, info *types.Info) {
   744  	var nestedList []*types.Named
   745  	for k, v := range info.Scopes {
   746  		switch k.(type) {
   747  		case *ast.BlockStmt, *ast.FuncType:
   748  			for _, name := range v.Names() {
   749  				obj := v.Lookup(name)
   750  				if named, ok := obj.Type().(*types.Named); ok && named.Obj().Pkg() == pkg {
   751  					nestedList = append(nestedList, named)
   752  				}
   753  			}
   754  		}
   755  	}
   756  	if len(nestedList) == 0 {
   757  		return
   758  	}
   759  	sort.Slice(nestedList, func(i, j int) bool {
   760  		return nestedList[i].Obj().Pos() < nestedList[j].Obj().Pos()
   761  	})
   762  	for i, named := range nestedList {
   763  		ctx.nestedMap[named] = i + 1
   764  	}
   765  }
   766  
   767  func RunFile(filename string, src interface{}, args []string, mode Mode) (exitCode int, err error) {
   768  	ctx := NewContext(mode)
   769  	return ctx.RunFile(filename, src, args)
   770  }
   771  
   772  func Run(path string, args []string, mode Mode) (exitCode int, err error) {
   773  	ctx := NewContext(mode)
   774  	return ctx.Run(path, args)
   775  }
   776  
   777  func RunTest(path string, args []string, mode Mode) error {
   778  	ctx := NewContext(mode)
   779  	return ctx.RunTest(path, args)
   780  }
   781  
   782  var (
   783  	builtinPkg = &Package{
   784  		Name:          "builtin",
   785  		Path:          "github.com/goplus/igop/builtin",
   786  		Deps:          make(map[string]string),
   787  		Interfaces:    map[string]reflect.Type{},
   788  		NamedTypes:    map[string]reflect.Type{},
   789  		AliasTypes:    map[string]reflect.Type{},
   790  		Vars:          map[string]reflect.Value{},
   791  		Funcs:         map[string]reflect.Value{},
   792  		TypedConsts:   map[string]TypedConst{},
   793  		UntypedConsts: map[string]UntypedConst{},
   794  	}
   795  	builtinPrefix = "Builtin_"
   796  )
   797  
   798  func init() {
   799  	RegisterPackage(builtinPkg)
   800  }
   801  
   802  func RegisterCustomBuiltin(key string, fn interface{}) error {
   803  	v := reflect.ValueOf(fn)
   804  	switch v.Kind() {
   805  	case reflect.Func:
   806  		if !strings.HasPrefix(key, builtinPrefix) {
   807  			key = builtinPrefix + key
   808  		}
   809  		builtinPkg.Funcs[key] = v
   810  		typ := v.Type()
   811  		for i := 0; i < typ.NumIn(); i++ {
   812  			checkBuiltinDeps(typ.In(i))
   813  		}
   814  		for i := 0; i < typ.NumOut(); i++ {
   815  			checkBuiltinDeps(typ.Out(i))
   816  		}
   817  		return nil
   818  	}
   819  	return ErrNoFunction
   820  }
   821  
   822  func checkBuiltinDeps(typ reflect.Type) {
   823  	if path := typ.PkgPath(); path != "" {
   824  		v := strings.Split(path, "/")
   825  		builtinPkg.Deps[path] = v[len(v)-1]
   826  	}
   827  }
   828  
   829  func builtinFuncList() []string {
   830  	var list []string
   831  	for k, v := range builtinPkg.Funcs {
   832  		if strings.HasPrefix(k, builtinPrefix) {
   833  			name := k[len(builtinPrefix):]
   834  			typ := v.Type()
   835  			var ins []string
   836  			var outs []string
   837  			var call []string
   838  			numIn := typ.NumIn()
   839  			numOut := typ.NumOut()
   840  			if typ.IsVariadic() {
   841  				for i := 0; i < numIn-1; i++ {
   842  					ins = append(ins, fmt.Sprintf("p%v %v", i, typ.In(i).String()))
   843  					call = append(call, fmt.Sprintf("p%v", i))
   844  				}
   845  				ins = append(ins, fmt.Sprintf("p%v ...%v", numIn-1, typ.In(numIn-1).Elem().String()))
   846  				call = append(call, fmt.Sprintf("p%v...", numIn-1))
   847  			} else {
   848  				for i := 0; i < numIn; i++ {
   849  					ins = append(ins, fmt.Sprintf("p%v %v", i, typ.In(i).String()))
   850  					call = append(call, fmt.Sprintf("p%v", i))
   851  				}
   852  			}
   853  			for i := 0; i < numOut; i++ {
   854  				outs = append(outs, typ.Out(i).String())
   855  			}
   856  			var str string
   857  			if numOut > 0 {
   858  				str = fmt.Sprintf("func %v(%v)(%v) { return builtin.%v(%v) }",
   859  					name, strings.Join(ins, ","), strings.Join(outs, ","), k, strings.Join(call, ","))
   860  			} else {
   861  				str = fmt.Sprintf("func %v(%v) { builtin.%v(%v) }",
   862  					name, strings.Join(ins, ","), k, strings.Join(call, ","))
   863  			}
   864  			list = append(list, str)
   865  		}
   866  	}
   867  	return list
   868  }
   869  
   870  func ParseBuiltin(fset *token.FileSet, pkg string) (*ast.File, error) {
   871  	list := builtinFuncList()
   872  	if len(list) == 0 {
   873  		return nil, os.ErrInvalid
   874  	}
   875  	var deps []string
   876  	for k := range builtinPkg.Deps {
   877  		deps = append(deps, strconv.Quote(k))
   878  	}
   879  	sort.Strings(deps)
   880  	src := fmt.Sprintf(`package %v
   881  import (
   882  	"github.com/goplus/igop/builtin"
   883  	%v
   884  )
   885  %v
   886  `, pkg, strings.Join(deps, "\n"), strings.Join(list, "\n"))
   887  	return parser.ParseFile(fset, "gossa_builtin.go", src, 0)
   888  }