gcexportdata

package
v0.28.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 4, 2024 License: BSD-3-Clause Imports: 10 Imported by: 261

Documentation

Overview

Package gcexportdata provides functions for reading and writing export data, which is a serialized description of the API of a Go package including the names, kinds, types, and locations of all exported declarations.

The standard Go compiler (cmd/compile) writes an export data file for each package it compiles, which it later reads when compiling packages that import the earlier one. The compiler must thus contain logic to both write and read export data. (See the "Export" section in the cmd/compile/README file.)

The Read function in this package can read files produced by the compiler, producing go/types data structures. As a matter of policy, Read supports export data files produced by only the last two Go releases plus tip; see https://go.dev/issue/68898. The export data files produced by the compiler contain additional details related to generics, inlining, and other optimizations that cannot be decoded by the Read function.

In files written by the compiler, the export data is not at the start of the file. Before calling Read, use NewReader to locate the desired portion of the file.

The Write function in this package encodes the exported API of a Go package (types.Package) as a file. Such files can be later decoded by Read, but cannot be consumed by the compiler.

Future changes

Although Read supports the formats written by both Write and the compiler, the two are quite different, and there is an open proposal (https://go.dev/issue/69491) to separate these APIs.

Under that proposal, this package would ultimately provide only the Read operation for compiler export data, which must be defined in this module (golang.org/x/tools), not in the standard library, to avoid version skew for developer tools that need to read compiler export data both before and after a Go release, such as from Go 1.23 to Go 1.24. Because this package lives in the tools module, clients can update their version of the module some time before the Go 1.24 release and rebuild and redeploy their tools, which will then be able to consume both Go 1.23 and Go 1.24 export data files, so they will work before and after the Go update. (See discussion at https://go.dev/issue/15651.)

The operations to import and export go/types data structures would be defined in the go/types package as Import and Export. Write would (eventually) delegate to Export, and Read, when it detects a file produced by Export, would delegate to Import.

Deprecations

The NewImporter and Find functions are deprecated and should not be used in new code. The WriteBundle and ReadBundle functions are experimental, and there is an open proposal to deprecate them (https://go.dev/issue/69573).

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Find deprecated

func Find(importPath, srcDir string) (filename, path string)

Find returns the name of an object (.o) or archive (.a) file containing type information for the specified import path, using the go command. If no file was found, an empty filename is returned.

A relative srcDir is interpreted relative to the current working directory.

Find also returns the package's resolved (canonical) import path, reflecting the effects of srcDir and vendoring on importPath.

Deprecated: Use the higher-level API in golang.org/x/tools/go/packages, which is more efficient.

func NewImporter deprecated

func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom

NewImporter returns a new instance of the types.Importer interface that reads type information from export data files written by gc. The Importer also satisfies types.ImporterFrom.

Export data files are located using "go build" workspace conventions and the build.Default context.

Use this importer instead of go/importer.For("gc", ...) to avoid the version-skew problems described in the documentation of this package, or to control the FileSet or access the imports map populated during package loading.

Deprecated: Use the higher-level API in golang.org/x/tools/go/packages, which is more efficient.

Example

ExampleNewImporter demonstrates usage of NewImporter to provide type information for dependencies when type-checking Go source code.

package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"go/types"
	"log"
	"path/filepath"

	"golang.org/x/tools/go/gcexportdata"
)

func main() {
	const src = `package myrpc

// choosing a package that doesn't change across releases
import "net/rpc"

const serverError rpc.ServerError = ""
`
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "myrpc.go", src, 0)
	if err != nil {
		log.Fatal(err)
	}

	packages := make(map[string]*types.Package)
	imp := gcexportdata.NewImporter(fset, packages)
	conf := types.Config{Importer: imp}
	pkg, err := conf.Check("myrpc", fset, []*ast.File{f}, nil)
	if err != nil {
		log.Fatal(err)
	}

	// object from imported package
	pi := packages["net/rpc"].Scope().Lookup("ServerError")
	fmt.Printf("type %s.%s %s // %s\n",
		pi.Pkg().Path(),
		pi.Name(),
		pi.Type().Underlying(),
		slashify(fset.Position(pi.Pos())),
	)

	// object in source package
	twopi := pkg.Scope().Lookup("serverError")
	fmt.Printf("const %s %s = %s // %s\n",
		twopi.Name(),
		twopi.Type(),
		twopi.(*types.Const).Val(),
		slashify(fset.Position(twopi.Pos())),
	)

}

func slashify(posn token.Position) token.Position {
	posn.Filename = filepath.ToSlash(posn.Filename)
	return posn
}
Output:


type net/rpc.ServerError string // $GOROOT/src/net/rpc/client.go:20:1
const serverError net/rpc.ServerError = "" // myrpc.go:6:7

func NewReader

func NewReader(r io.Reader) (io.Reader, error)

NewReader returns a reader for the export data section of an object (.o) or archive (.a) file read from r. The new reader may provide additional trailing data beyond the end of the export data.

func Read

func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error)

Read reads export data from in, decodes it, and returns type information for the package.

Read is capable of reading export data produced by Write at the same source code version, or by the last two Go releases (plus tip) of the standard Go compiler. Reading files from older compilers may produce an error.

The package path (effectively its linker symbol prefix) is specified by path, since unlike the package name, this information may not be recorded in the export data.

File position information is added to fset.

Read may inspect and add to the imports map to ensure that references within the export data to other packages are consistent. The caller must ensure that imports[path] does not exist, or exists but is incomplete (see types.Package.Complete), and Read inserts the resulting package into this map entry.

On return, the state of the reader is undefined.

Example

ExampleRead uses gcexportdata.Read to load type information for the "fmt" package from the fmt.a file produced by the gc compiler.

package main

import (
	"fmt"
	"go/token"
	"go/types"
	"log"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/tools/go/gcexportdata"
)

func main() {
	// Find the export data file.
	filename, path := gcexportdata.Find("fmt", "")
	if filename == "" {
		log.Fatalf("can't find export data for fmt")
	}
	fmt.Printf("Package path:       %s\n", path)

	// Open and read the file.
	f, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	r, err := gcexportdata.NewReader(f)
	if err != nil {
		log.Fatalf("reading export data %s: %v", filename, err)
	}

	// Decode the export data.
	fset := token.NewFileSet()
	imports := make(map[string]*types.Package)
	pkg, err := gcexportdata.Read(r, fset, imports, path)
	if err != nil {
		log.Fatal(err)
	}

	// We can see all the names in Names.
	members := pkg.Scope().Names()
	foundPrintln := false
	for _, member := range members {
		if member == "Println" {
			foundPrintln = true
			break
		}
	}
	fmt.Print("Package members:    ")
	if foundPrintln {
		fmt.Println("Println found")
	} else {
		fmt.Println("Println not found")
	}

	// We can also look up a name directly using Lookup.
	println := pkg.Scope().Lookup("Println")
	// go 1.18+ uses the 'any' alias
	typ := strings.ReplaceAll(println.Type().String(), "interface{}", "any")
	fmt.Printf("Println type:       %s\n", typ)
	posn := fset.Position(println.Pos())
	// make example deterministic
	posn.Line = 123
	fmt.Printf("Println location:   %s\n", slashify(posn))

}

func slashify(posn token.Position) token.Position {
	posn.Filename = filepath.ToSlash(posn.Filename)
	return posn
}
Output:


Package path:       fmt
Package members:    Println found
Println type:       func(a ...any) (n int, err error)
Println location:   $GOROOT/src/fmt/print.go:123:1

func ReadBundle added in v0.1.1

func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error)

ReadBundle reads an export bundle from in, decodes it, and returns type information for the packages. File position information is added to fset.

ReadBundle may inspect and add to the imports map to ensure that references within the export bundle to other packages are consistent.

On return, the state of the reader is undefined.

Experimental: This API is experimental and may change in the future.

func Write

func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error

Write writes encoded type information for the specified package to out. The FileSet provides file position information for named objects.

func WriteBundle added in v0.1.1

func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error

WriteBundle writes encoded type information for the specified packages to out. The FileSet provides file position information for named objects.

Experimental: This API is experimental and may change in the future.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL