github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/sources.go (about) 1 package compiler 2 3 import ( 4 "go/ast" 5 "go/token" 6 "go/types" 7 "sort" 8 9 "github.com/neelance/astrewrite" 10 ) 11 12 // sources is a slice of parsed Go sources. 13 // 14 // Note that the sources would normally belong to a single logical Go package, 15 // but they don't have to be a real Go package (i.e. found on the file system) 16 // or represent a complete package (i.e. it could be only a few source files 17 // compiled by `gopherjs build foo.go bar.go`). 18 type sources struct { 19 // ImportPath representing the sources, if exists. May be empty for "virtual" 20 // packages like testmain or playground-generated package. 21 ImportPath string 22 Files []*ast.File 23 FileSet *token.FileSet 24 } 25 26 // Sort the Files slice by the original source name to ensure consistent order 27 // of processing. This is required for reproducible JavaScript output. 28 // 29 // Note this function mutates the original slice. 30 func (s sources) Sort() sources { 31 sort.Slice(s.Files, func(i, j int) bool { 32 return s.FileSet.File(s.Files[i].Pos()).Name() > s.FileSet.File(s.Files[j].Pos()).Name() 33 }) 34 return s 35 } 36 37 // Simplified returns a new sources instance with each Files entry processed by 38 // astrewrite.Simplify. 39 func (s sources) Simplified(typesInfo *types.Info) sources { 40 simplified := sources{ 41 ImportPath: s.ImportPath, 42 FileSet: s.FileSet, 43 } 44 for _, file := range s.Files { 45 simplified.Files = append(simplified.Files, astrewrite.Simplify(file, typesInfo, false)) 46 } 47 return simplified 48 } 49 50 // TypeCheck the sources. Returns information about declared package types and 51 // type information for the supplied AST. 52 func (s sources) TypeCheck(importContext *ImportContext, tContext *types.Context) (*types.Info, *types.Package, error) { 53 const errLimit = 10 // Max number of type checking errors to return. 54 55 typesInfo := &types.Info{ 56 Types: make(map[ast.Expr]types.TypeAndValue), 57 Defs: make(map[*ast.Ident]types.Object), 58 Uses: make(map[*ast.Ident]types.Object), 59 Implicits: make(map[ast.Node]types.Object), 60 Selections: make(map[*ast.SelectorExpr]*types.Selection), 61 Scopes: make(map[ast.Node]*types.Scope), 62 Instances: make(map[*ast.Ident]types.Instance), 63 } 64 65 var typeErrs ErrorList 66 67 importer := packageImporter{ImportContext: importContext} 68 69 config := &types.Config{ 70 Context: tContext, 71 Importer: &importer, 72 Sizes: sizes32, 73 Error: func(err error) { typeErrs = typeErrs.AppendDistinct(err) }, 74 } 75 typesPkg, err := config.Check(s.ImportPath, s.FileSet, s.Files, typesInfo) 76 // If we encountered any import errors, it is likely that the other type errors 77 // are not meaningful and would be resolved by fixing imports. Return them 78 // separately, if any. https://github.com/gopherjs/gopherjs/issues/119. 79 if importer.Errors.ErrOrNil() != nil { 80 return nil, nil, importer.Errors.Trim(errLimit).ErrOrNil() 81 } 82 // Return any other type errors. 83 if typeErrs.ErrOrNil() != nil { 84 return nil, nil, typeErrs.Trim(errLimit).ErrOrNil() 85 } 86 // Any general errors that may have occurred during type checking. 87 if err != nil { 88 return nil, nil, err 89 } 90 return typesInfo, typesPkg, nil 91 } 92 93 // ParseGoLinknames extracts all //go:linkname compiler directive from the sources. 94 func (s sources) ParseGoLinknames() ([]GoLinkname, error) { 95 goLinknames := []GoLinkname{} 96 var errs ErrorList 97 for _, file := range s.Files { 98 found, err := parseGoLinknames(s.FileSet, s.ImportPath, file) 99 errs = errs.Append(err) 100 goLinknames = append(goLinknames, found...) 101 } 102 return goLinknames, errs.ErrOrNil() 103 } 104 105 // packageImporter implements go/types.Importer interface. 106 type packageImporter struct { 107 ImportContext *ImportContext 108 Errors ErrorList 109 } 110 111 func (pi *packageImporter) Import(path string) (*types.Package, error) { 112 if path == "unsafe" { 113 return types.Unsafe, nil 114 } 115 116 a, err := pi.ImportContext.Import(path) 117 if err != nil { 118 pi.Errors = pi.Errors.AppendDistinct(err) 119 return nil, err 120 } 121 122 return pi.ImportContext.Packages[a.ImportPath], nil 123 }