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