github.com/goplus/gossa@v0.3.25/run.go (about)

     1  //go:build ignore
     2  // +build ignore
     3  
     4  package gossa
     5  
     6  import (
     7  	"errors"
     8  	"flag"
     9  	"fmt"
    10  	"go/ast"
    11  	"go/build"
    12  	"go/importer"
    13  	"go/parser"
    14  	"go/token"
    15  	"go/types"
    16  	"os"
    17  	"path/filepath"
    18  	"reflect"
    19  	"strconv"
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/goplus/gossa/internal/gopfile"
    24  	"github.com/goplus/reflectx"
    25  	"golang.org/x/tools/go/packages"
    26  	"golang.org/x/tools/go/ssa"
    27  	"golang.org/x/tools/go/ssa/ssautil"
    28  )
    29  
    30  var (
    31  	ErrNoPackage    = errors.New("no package")
    32  	ErrPackage      = errors.New("package contain errors")
    33  	ErrNotFoundMain = errors.New("not found main package")
    34  )
    35  
    36  var (
    37  	UnsafeSizes types.Sizes
    38  )
    39  
    40  // func loadFile2(input string, src interface{}) (*ssa.Package, error) {
    41  // 	if !filepath.IsAbs(input) {
    42  // 		wd, _ := os.Getwd()
    43  // 		input = filepath.Join(wd, input)
    44  // 	}
    45  // 	const mode = parser.AllErrors | parser.ParseComments
    46  // 	fset := token.NewFileSet()
    47  // 	f, err := parser.ParseFile(fset, input, src, mode)
    48  // 	if err != nil {
    49  // 		return nil, err
    50  // 	}
    51  // 	cfg := &loader.Config{}
    52  // 	cfg.Fset = fset
    53  // 	cfg.CreateFromFiles(input, f)
    54  // 	iprog, err := cfg.Load()
    55  // 	if err != nil {
    56  // 		return nil, err
    57  // 	}
    58  // 	prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
    59  // 	prog.Build()
    60  // 	var mainPkg *ssa.Package
    61  // 	if len(iprog.Created) > 0 {
    62  // 		mainPkg = prog.Package(iprog.Created[0].Pkg)
    63  // 	} else {
    64  // 		if pkgs := ssautil.MainPackages(prog.AllPackages()); len(pkgs) > 0 {
    65  // 			mainPkg = pkgs[0]
    66  // 		}
    67  // 	}
    68  // 	if mainPkg == nil {
    69  // 		return nil, ErrNotFoundMain
    70  // 	}
    71  // 	return mainPkg, nil
    72  // }
    73  
    74  // func LoadAst(pkg *ast.Package) (*ssa.Package, error) {
    75  // 	pkg := types.NewPackage(f.Name.Name, "")
    76  // 	var chkerr error
    77  // 	ssapkg, _, err := BuildPackage(DefaultLoader, fset, pkg, []*ast.File{f}, ssa.SanityCheckFunctions) // ssa.NaiveForm) //ssa.SanityCheckFunctions)
    78  // 	if chkerr != nil {
    79  // 		return nil, chkerr
    80  // 	}
    81  // 	if err != nil {
    82  // 		return nil, err
    83  // 	}
    84  // 	ssapkg.Build()
    85  // }
    86  
    87  func LoadFile(input string, src interface{}) (*ssa.Package, error) {
    88  	if !filepath.IsAbs(input) {
    89  		wd, _ := os.Getwd()
    90  		input = filepath.Join(wd, input)
    91  	}
    92  	const mode = parser.AllErrors | parser.ParseComments
    93  	fset := token.NewFileSet()
    94  	f, err := parser.ParseFile(fset, input, src, mode)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	// if f.Name.Name != "main" {
    99  	// 	return nil, ErrNotFoundMain
   100  	// }
   101  	var hasOtherPkgs bool
   102  	for _, im := range f.Imports {
   103  		v, _ := strconv.Unquote(im.Path.Value)
   104  		if !externPackages[v] {
   105  			hasOtherPkgs = true
   106  			break
   107  		}
   108  	}
   109  	if !hasOtherPkgs {
   110  		pkg := types.NewPackage(f.Name.Name, "")
   111  		var chkerr error
   112  		ssapkg, _, err := BuildPackage(DefaultLoader, fset, pkg, []*ast.File{f}, ssa.SanityCheckFunctions) // ssa.NaiveForm) //ssa.SanityCheckFunctions)
   113  		if chkerr != nil {
   114  			return nil, chkerr
   115  		}
   116  		if err != nil {
   117  			return nil, err
   118  		}
   119  		ssapkg.Build()
   120  		return ssapkg, nil
   121  	}
   122  	cfg := &packages.Config{
   123  		Fset: fset,
   124  		Mode: packages.NeedName | packages.NeedDeps | packages.LoadTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedTypesSizes,
   125  	}
   126  	cfg.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
   127  		if filename == input {
   128  			return f, nil
   129  		}
   130  		return parser.ParseFile(fset, filename, src, mode)
   131  	}
   132  	list, err := packages.Load(cfg, input)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	if len(list) == 0 {
   137  		return nil, ErrNoPackage
   138  	}
   139  	if packages.PrintErrors(list) > 0 {
   140  		return nil, ErrPackage
   141  	}
   142  	list[0].ID = "main"
   143  	list[0].PkgPath = "main"
   144  	// hack fix types.Types.Path() command-line-arguments
   145  	v := reflect.ValueOf(list[0].Types).Elem()
   146  	reflectx.FieldX(v, 0).SetString("main")
   147  	prog, pkgs := ssautil.AllPackages(list, ssa.NaiveForm)
   148  	prog.Build()
   149  	mainPkgs := ssautil.MainPackages(pkgs)
   150  	if len(mainPkgs) == 0 {
   151  		return nil, ErrNotFoundMain
   152  	}
   153  	return mainPkgs[0], nil
   154  }
   155  
   156  func loadPkg(input string) (*ssa.Package, error) {
   157  	wd, _ := os.Getwd()
   158  	p, err := build.Import(input, wd, 0)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	if p.Name != "main" {
   163  		return nil, ErrNotFoundMain
   164  	}
   165  	var hasOtherPkgs bool
   166  	for _, im := range p.Imports {
   167  		if !externPackages[im] {
   168  			hasOtherPkgs = true
   169  			break
   170  		}
   171  	}
   172  	if !hasOtherPkgs {
   173  		const mode = parser.AllErrors | parser.ParseComments
   174  		fset := token.NewFileSet()
   175  		var files []*ast.File
   176  		for _, fname := range p.GoFiles {
   177  			filename := filepath.Join(p.Dir, fname)
   178  			f, err := parser.ParseFile(fset, filename, nil, mode)
   179  			if err != nil {
   180  				return nil, err
   181  			}
   182  			files = append(files, f)
   183  		}
   184  		pkg := types.NewPackage("main", "")
   185  		var chkerr error
   186  		ssapkg, _, err := ssautil.BuildPackage(&types.Config{
   187  			Importer: importer.Default(),
   188  			Error: func(err error) {
   189  				fmt.Fprintln(os.Stderr, err)
   190  				chkerr = ErrPackage
   191  			},
   192  		}, fset, pkg, files, ssa.SanityCheckFunctions)
   193  		if chkerr != nil {
   194  			return nil, chkerr
   195  		}
   196  		if err != nil {
   197  			return nil, err
   198  		}
   199  		ssapkg.Build()
   200  		return ssapkg, nil
   201  	}
   202  	cfg := &packages.Config{
   203  		Mode: packages.NeedName | packages.NeedDeps | packages.LoadTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedTypesSizes,
   204  	}
   205  	list, err := packages.Load(cfg, input)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	if len(list) == 0 {
   210  		return nil, ErrNoPackage
   211  	}
   212  	if packages.PrintErrors(list) > 0 {
   213  		return nil, ErrPackage
   214  	}
   215  	prog, pkgs := ssautil.AllPackages(list, ssa.SanityCheckFunctions)
   216  	prog.Build()
   217  	mainPkgs := ssautil.MainPackages(pkgs)
   218  	if len(mainPkgs) == 0 {
   219  		return nil, ErrNotFoundMain
   220  	}
   221  	return mainPkgs[0], nil
   222  }
   223  
   224  func Run(mode Mode, input string, args []string) error {
   225  	if strings.HasSuffix(input, ".go") {
   226  		return RunFile(mode, input, nil, args)
   227  	}
   228  	pkg, err := loadPkg(input)
   229  	if err != nil {
   230  		return err
   231  	}
   232  	return RunPkg(pkg, mode, input, "main", args)
   233  }
   234  
   235  func foundPkg(pkg string) (*build.Package, error) {
   236  	if filepath.IsAbs(pkg) {
   237  		return build.ImportDir(pkg, build.FindOnly)
   238  	} else {
   239  		return build.Import(pkg, ".", build.FindOnly)
   240  	}
   241  }
   242  
   243  func RunTest(mode Mode, input string, args []string) error {
   244  	p, err := foundPkg(input)
   245  	if err != nil {
   246  		return fmt.Errorf("not found pkg: %v", err)
   247  	}
   248  	if p.Dir != "." {
   249  		os.Chdir(p.Dir)
   250  	}
   251  	pkgpath, pkgs, err := LoadTest(".")
   252  	if err != nil {
   253  		return err
   254  	}
   255  	if len(pkgs) == 0 {
   256  		fmt.Printf("?\t%s [no test files]\n", pkgpath)
   257  		return nil
   258  	}
   259  	RunTestPkg(pkgs, mode, pkgpath, args)
   260  	return nil
   261  }
   262  
   263  func RunFile(mode Mode, filename string, src interface{}, args []string) error {
   264  	ext := filepath.Ext(filename)
   265  	switch ext {
   266  	case ".gop":
   267  		data, err := gopfile.Build(filename, src)
   268  		if err != nil {
   269  			return err
   270  		}
   271  		src = data
   272  	}
   273  	pkg, err := LoadFile(filename, src)
   274  	if err != nil {
   275  		return err
   276  	}
   277  	return RunPkg(pkg, mode, filename, "main", args)
   278  }
   279  
   280  func LoadTest(input string) (string, []*ssa.Package, error) {
   281  	cfg := &packages.Config{
   282  		Mode:  packages.NeedName | packages.NeedDeps | packages.LoadTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedTypesSizes,
   283  		Tests: true,
   284  	}
   285  	list, err := packages.Load(cfg, input)
   286  	if err != nil {
   287  		return "", nil, err
   288  	}
   289  	var foundError bool
   290  	for _, v := range list {
   291  		if len(v.Errors) > 0 {
   292  			for _, err := range v.Errors {
   293  				fmt.Fprintln(os.Stderr, err)
   294  			}
   295  			foundError = true
   296  		}
   297  	}
   298  	if foundError {
   299  		return "", nil, errors.New("build failed")
   300  	}
   301  	prog, ppkgs := ssautil.AllPackages(list, ssa.SanityCheckFunctions)
   302  	prog.Build()
   303  	var pkgs []*ssa.Package
   304  	for _, p := range ppkgs {
   305  		if p == nil {
   306  			continue
   307  		}
   308  		pkgs = append(pkgs, p)
   309  	}
   310  	if len(pkgs) == 0 {
   311  		return "", nil, errors.New("not found package")
   312  	}
   313  	return pkgs[0].Pkg.Path(), pkgs, nil
   314  }
   315  
   316  func RunTestPkg(pkgs []*ssa.Package, mode Mode, input string, args []string) {
   317  	var testPkgs []*ssa.Package
   318  	for _, pkg := range pkgs {
   319  		if p := CreateTestMainPackage(pkg); p != nil {
   320  			testPkgs = append(testPkgs, p)
   321  		}
   322  	}
   323  	os.Args = []string{input}
   324  	if args != nil {
   325  		os.Args = append(os.Args, args...)
   326  	}
   327  	var failed bool
   328  	start := time.Now()
   329  	defer func() {
   330  		sec := time.Since(start).Seconds()
   331  		if failed {
   332  			fmt.Printf("FAIL\t%s %0.3fs\n", input, sec)
   333  		} else {
   334  			fmt.Printf("ok\t%s %0.3fs\n", input, sec)
   335  		}
   336  	}()
   337  	if len(testPkgs) == 0 {
   338  		fmt.Println("testing: warning: no tests to run")
   339  	}
   340  	for _, pkg := range testPkgs {
   341  		flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
   342  		if code := _Interpret(pkg, mode, "main"); code != 0 {
   343  			failed = true
   344  		}
   345  	}
   346  }
   347  
   348  func RunPkg(mainPkg *ssa.Package, mode Mode, input string, entry string, args []string) error {
   349  	// reset os args and flag
   350  	os.Args = []string{input}
   351  	if args != nil {
   352  		os.Args = append(os.Args, args...)
   353  	}
   354  	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
   355  
   356  	interp := NewInterp(DefaultLoader, mainPkg, mode)
   357  	interp.Run("init")
   358  	_, exitCode := interp.Run(entry)
   359  	if exitCode != 0 {
   360  		return fmt.Errorf("interpreting %v: exit code was %d", input, exitCode)
   361  	}
   362  	return nil
   363  }