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 }