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  }