gcimporter.go raw

   1  // Copyright 2011 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  // This file is a reduced copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go.
   6  
   7  // Package gcimporter provides various functions for reading
   8  // gc-generated object files that can be used to implement the
   9  // Importer interface defined by the Go 1.5 standard library package.
  10  //
  11  // The encoding is deterministic: if the encoder is applied twice to
  12  // the same types.Package data structure, both encodings are equal.
  13  // This property may be important to avoid spurious changes in
  14  // applications such as build systems.
  15  //
  16  // However, the encoder is not necessarily idempotent. Importing an
  17  // exported package may yield a types.Package that, while it
  18  // represents the same set of Go types as the original, may differ in
  19  // the details of its internal representation. Because of these
  20  // differences, re-encoding the imported package may yield a
  21  // different, but equally valid, encoding of the package.
  22  package gcimporter // import "golang.org/x/tools/internal/gcimporter"
  23  
  24  import (
  25  	"bufio"
  26  	"fmt"
  27  	"go/token"
  28  	"go/types"
  29  	"io"
  30  	"os"
  31  )
  32  
  33  const (
  34  	// Enable debug during development: it adds some additional checks, and
  35  	// prevents errors from being recovered.
  36  	debug = false
  37  
  38  	// If trace is set, debugging output is printed to std out.
  39  	trace = false
  40  )
  41  
  42  // Import imports a gc-generated package given its import path and srcDir, adds
  43  // the corresponding package object to the packages map, and returns the object.
  44  // The packages map must contain all packages already imported.
  45  //
  46  // Import is only used in tests.
  47  func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
  48  	var rc io.ReadCloser
  49  	var id string
  50  	if lookup != nil {
  51  		// With custom lookup specified, assume that caller has
  52  		// converted path to a canonical import path for use in the map.
  53  		if path == "unsafe" {
  54  			return types.Unsafe, nil
  55  		}
  56  		id = path
  57  
  58  		// No need to re-import if the package was imported completely before.
  59  		if pkg = packages[id]; pkg != nil && pkg.Complete() {
  60  			return
  61  		}
  62  		f, err := lookup(path)
  63  		if err != nil {
  64  			return nil, err
  65  		}
  66  		rc = f
  67  	} else {
  68  		var filename string
  69  		filename, id, err = FindPkg(path, srcDir)
  70  		if filename == "" {
  71  			if path == "unsafe" {
  72  				return types.Unsafe, nil
  73  			}
  74  			return nil, err
  75  		}
  76  
  77  		// no need to re-import if the package was imported completely before
  78  		if pkg = packages[id]; pkg != nil && pkg.Complete() {
  79  			return
  80  		}
  81  
  82  		// open file
  83  		f, err := os.Open(filename)
  84  		if err != nil {
  85  			return nil, err
  86  		}
  87  		defer func() {
  88  			if err != nil {
  89  				// add file name to error
  90  				err = fmt.Errorf("%s: %v", filename, err)
  91  			}
  92  		}()
  93  		rc = f
  94  	}
  95  	defer rc.Close()
  96  
  97  	buf := bufio.NewReader(rc)
  98  	data, err := ReadUnified(buf)
  99  	if err != nil {
 100  		err = fmt.Errorf("import %q: %v", path, err)
 101  		return
 102  	}
 103  
 104  	// unified: emitted by cmd/compile since go1.20.
 105  	_, pkg, err = UImportData(fset, packages, data, id)
 106  
 107  	return
 108  }
 109