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 }