github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/go/internal/gcimporter/gcimporter.go (about)

     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  // Package gcimporter implements Import for gc-generated object files.
     6  package gcimporter // import "go/internal/gcimporter"
     7  
     8  import (
     9  	"bufio"
    10  	"fmt"
    11  	"go/build"
    12  	"go/token"
    13  	"go/types"
    14  	"io"
    15  	"io/ioutil"
    16  	"os"
    17  	"path/filepath"
    18  	"strings"
    19  )
    20  
    21  // debugging/development support
    22  const debug = false
    23  
    24  var pkgExts = [...]string{".a", ".o"}
    25  
    26  // FindPkg returns the filename and unique package id for an import
    27  // path based on package information provided by build.Import (using
    28  // the build.Default build.Context). A relative srcDir is interpreted
    29  // relative to the current working directory.
    30  // If no file was found, an empty filename is returned.
    31  //
    32  func FindPkg(path, srcDir string) (filename, id string) {
    33  	if path == "" {
    34  		return
    35  	}
    36  
    37  	var noext string
    38  	switch {
    39  	default:
    40  		// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
    41  		// Don't require the source files to be present.
    42  		if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
    43  			srcDir = abs
    44  		}
    45  		bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
    46  		if bp.PkgObj == "" {
    47  			id = path // make sure we have an id to print in error message
    48  			return
    49  		}
    50  		noext = strings.TrimSuffix(bp.PkgObj, ".a")
    51  		id = bp.ImportPath
    52  
    53  	case build.IsLocalImport(path):
    54  		// "./x" -> "/this/directory/x.ext", "/this/directory/x"
    55  		noext = filepath.Join(srcDir, path)
    56  		id = noext
    57  
    58  	case filepath.IsAbs(path):
    59  		// for completeness only - go/build.Import
    60  		// does not support absolute imports
    61  		// "/x" -> "/x.ext", "/x"
    62  		noext = path
    63  		id = path
    64  	}
    65  
    66  	if false { // for debugging
    67  		if path != id {
    68  			fmt.Printf("%s -> %s\n", path, id)
    69  		}
    70  	}
    71  
    72  	// try extensions
    73  	for _, ext := range pkgExts {
    74  		filename = noext + ext
    75  		if f, err := os.Stat(filename); err == nil && !f.IsDir() {
    76  			return
    77  		}
    78  	}
    79  
    80  	filename = "" // not found
    81  	return
    82  }
    83  
    84  // Import imports a gc-generated package given its import path and srcDir, adds
    85  // the corresponding package object to the packages map, and returns the object.
    86  // The packages map must contain all packages already imported.
    87  //
    88  func Import(packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
    89  	var rc io.ReadCloser
    90  	var id string
    91  	if lookup != nil {
    92  		// With custom lookup specified, assume that caller has
    93  		// converted path to a canonical import path for use in the map.
    94  		if path == "unsafe" {
    95  			return types.Unsafe, nil
    96  		}
    97  		id = path
    98  
    99  		// No need to re-import if the package was imported completely before.
   100  		if pkg = packages[id]; pkg != nil && pkg.Complete() {
   101  			return
   102  		}
   103  		f, err := lookup(path)
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  		rc = f
   108  	} else {
   109  		var filename string
   110  		filename, id = FindPkg(path, srcDir)
   111  		if filename == "" {
   112  			if path == "unsafe" {
   113  				return types.Unsafe, nil
   114  			}
   115  			return nil, fmt.Errorf("can't find import: %q", id)
   116  		}
   117  
   118  		// no need to re-import if the package was imported completely before
   119  		if pkg = packages[id]; pkg != nil && pkg.Complete() {
   120  			return
   121  		}
   122  
   123  		// open file
   124  		f, err := os.Open(filename)
   125  		if err != nil {
   126  			return nil, err
   127  		}
   128  		defer func() {
   129  			if err != nil {
   130  				// add file name to error
   131  				err = fmt.Errorf("%s: %v", filename, err)
   132  			}
   133  		}()
   134  		rc = f
   135  	}
   136  	defer func() {
   137  		rc.Close()
   138  	}()
   139  
   140  	var hdr string
   141  	buf := bufio.NewReader(rc)
   142  	if hdr, err = FindExportData(buf); err != nil {
   143  		return
   144  	}
   145  
   146  	switch hdr {
   147  	case "$$\n":
   148  		err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path)
   149  	case "$$B\n":
   150  		var data []byte
   151  		data, err = ioutil.ReadAll(buf)
   152  		if err == nil {
   153  			// TODO(gri): allow clients of go/importer to provide a FileSet.
   154  			// Or, define a new standard go/types/gcexportdata package.
   155  			fset := token.NewFileSet()
   156  			_, pkg, err = BImportData(fset, packages, data, id)
   157  			return
   158  		}
   159  	default:
   160  		err = fmt.Errorf("unknown export data header: %q", hdr)
   161  	}
   162  
   163  	return
   164  }
   165  
   166  func deref(typ types.Type) types.Type {
   167  	if p, _ := typ.(*types.Pointer); p != nil {
   168  		return p.Elem()
   169  	}
   170  	return typ
   171  }
   172  
   173  type byPath []*types.Package
   174  
   175  func (a byPath) Len() int           { return len(a) }
   176  func (a byPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   177  func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }