// Test to ensure that gcexportdata can read files produced by App
// Engine Go runtime v1.6.
func TestAppEngine16(t *testing.T) {
	// Open and read the file.
	f, err := os.Open("testdata/errors-ae16.a")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	r, err := gcexportdata.NewReader(f)
	if err != nil {
		log.Fatalf("reading export data: %v", err)
	}

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

	// Print package information.
	got := pkg.Scope().Lookup("New").Type().String()
	want := "func(text string) error"
	if got != want {
		t.Errorf("New.Type = %s, want %s", got, want)
	}
}
Beispiel #2
0
// ExampleRead uses gcexportdata.Read to load type information for the
// "fmt" package from the fmt.a file produced by the gc compiler.
func ExampleRead() {
	// 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)
	fmt.Printf("Export data:        %s\n", filepath.Base(filename))

	// 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)
	}

	// Print package information.
	fmt.Printf("Package members:    %s...\n", pkg.Scope().Names()[:5])
	println := pkg.Scope().Lookup("Println")
	posn := fset.Position(println.Pos())
	posn.Line = 123 // make example deterministic
	fmt.Printf("Println type:       %s\n", println.Type())
	fmt.Printf("Println location:   %s\n", slashify(posn))

	// Output:
	//
	// Package path:       fmt
	// Export data:        fmt.a
	// Package members:    [Errorf Formatter Fprint Fprintf Fprintln]...
	// Println type:       func(a ...interface{}) (n int, err error)
	// Println location:   $GOROOT/src/fmt/print.go:123:1
}
Beispiel #3
0
// Resolve resolves the package information for unit and its dependencies.  On
// success the package corresponding to unit is located via ImportPath in the
// Packages map of the returned value.
//
// If info != nil, it is used to populate the Info field of the return value
// and will contain the output of the type checker in each user-provided map
// field.
func Resolve(unit *apb.CompilationUnit, f Fetcher, info *types.Info) (*PackageInfo, error) {
	isSource := make(map[string]bool)
	for _, src := range unit.SourceFile {
		isSource[src] = true
	}

	deps := make(map[string]*types.Package) // import path → package
	imap := make(map[string]*spb.VName)     // import path → vname
	srcs := make(map[string]string)         // file path → text
	fset := token.NewFileSet()              // location info for the parser
	var files []*ast.File                   // parsed sources

	// Classify the required inputs as either sources, which are to be parsed,
	// or dependencies, which are to be "imported" via the type-checker's
	// import mechanism.  If successful, this populates fset and files with the
	// lexical and syntactic content of the package's own sources.
	for _, ri := range unit.RequiredInput {
		if ri.Info == nil {
			return nil, errors.New("required input file info missing")
		}

		// Fetch the contents of each required input.
		fpath := ri.Info.Path
		data, err := f.Fetch(fpath, ri.Info.Digest)
		if err != nil {
			return nil, fmt.Errorf("fetching %q (%s): %v", fpath, ri.Info.Digest, err)
		}

		// Source inputs need to be parsed, so we can give their ASTs to the
		// type checker later on.
		if isSource[fpath] {
			srcs[fpath] = string(data)
			parsed, err := parser.ParseFile(fset, fpath, data, parser.AllErrors)
			if err != nil {
				return nil, fmt.Errorf("parsing %q: %v", fpath, err)
			}
			files = append(files, parsed)
			continue
		}

		// For archives, recover the import path from the VName and read the
		// archive header for type information.  If the VName is not set, the
		// import is considered bogus.
		if ri.VName == nil {
			return nil, fmt.Errorf("missing vname for %q", fpath)
		}
		r, err := gcexportdata.NewReader(bytes.NewReader(data))
		if err != nil {
			return nil, fmt.Errorf("scanning export data in %q: %v", fpath, err)
		}

		ipath := path.Join(ri.VName.Corpus, ri.VName.Path)
		if govname.IsStandardLibrary(ri.VName) {
			ipath = ri.VName.Path
		}
		imap[ipath] = ri.VName

		// Populate deps with package ipath and its prerequisites.
		if _, err := gcexportdata.Read(r, fset, deps, ipath); err != nil {
			return nil, fmt.Errorf("importing %q: %v", ipath, err)
		}
	}

	// Fill in the mapping from packages to vnames.
	vmap := make(map[*types.Package]*spb.VName)
	for ip, vname := range imap {
		if pkg := deps[ip]; pkg != nil {
			vmap[pkg] = vname
		}
	}
	pi := &PackageInfo{
		Name:         files[0].Name.Name,
		ImportPath:   path.Join(unit.VName.Corpus, unit.VName.Path),
		VNames:       vmap,
		FileSet:      fset,
		Files:        files,
		SourceText:   srcs,
		Dependencies: deps,
		Info:         info,

		sigs: make(map[types.Object]string),
	}

	// Run the type-checker and collect any errors it generates.  Errors in the
	// type checker are not returned directly; the caller can read them from
	// the Errors field.
	c := &types.Config{
		FakeImportC:              true, // so we can handle cgo
		DisableUnusedImportCheck: true, // this is not fatal to type-checking
		Importer:                 pi,
		Error: func(err error) {
			pi.Errors = append(pi.Errors, err)
		},
	}
	if pkg, _ := c.Check(pi.Name, pi.FileSet, pi.Files, pi.Info); pkg != nil {
		pi.Package = pkg
		vmap[pkg] = unit.VName
	}
	return pi, nil
}