]> Cypherpunks repositories - gostls13.git/commitdiff
exp/types: generalized GCImporter API.
authorRobert Griesemer <gri@golang.org>
Mon, 26 Mar 2012 18:26:05 +0000 (11:26 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 26 Mar 2012 18:26:05 +0000 (11:26 -0700)
- Renamed ExportData -> FindGcExportData
  and base it on an a bufio.Reader rather
  than a filename so it can be used in
  environments where object files are
  stored elsewhere.

- Factor former GcImporter into GcImportData
  and GcImport. Implementations with different
  storage locations for object files can build
  a customized GcImport using GcImportData.

This is pkg/exp only - no impact on Go 1.

R=golang-dev, lvd, rsc
CC=golang-dev
https://golang.org/cl/5574069

src/pkg/exp/gotype/gotype.go
src/pkg/exp/types/check_test.go
src/pkg/exp/types/exportdata.go
src/pkg/exp/types/gcimporter.go
src/pkg/exp/types/gcimporter_test.go

index 30eaf22fca6b270a33a6be37d6b6ec17f1174c23..3aca40e8e74d0ee98301fb118e9ffe87ee8291c6 100644 (file)
@@ -171,7 +171,7 @@ func processFiles(filenames []string, allFiles bool) {
 
 func processPackage(fset *token.FileSet, files map[string]*ast.File) {
        // make a package (resolve all identifiers)
-       pkg, err := ast.NewPackage(fset, files, types.GcImporter, types.Universe)
+       pkg, err := ast.NewPackage(fset, files, types.GcImport, types.Universe)
        if err != nil {
                report(err)
                return
index 0e20646a005bc188a1d2fe34108007a507f3ab3a..34c26c9908cff68c55735009192f9a9a5dd0d0e1 100644 (file)
@@ -184,7 +184,7 @@ func check(t *testing.T, testname string, testfiles []string) {
        eliminate(t, errors, err)
 
        // verify errors returned after resolving identifiers
-       pkg, err := ast.NewPackage(fset, files, GcImporter, Universe)
+       pkg, err := ast.NewPackage(fset, files, GcImport, Universe)
        eliminate(t, errors, err)
 
        // verify errors returned by the typechecker
index fa5b6a37fe80d05c4e6f2fcb6f29650ca607a92c..bca2038804e0c23b7780842db45cd2849156efaf 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// This file implements ExportData.
+// This file implements FindGcExportData.
 
 package types
 
@@ -11,15 +11,14 @@ import (
        "errors"
        "fmt"
        "io"
-       "os"
        "strconv"
        "strings"
 )
 
-func readGopackHeader(buf *bufio.Reader) (name string, size int, err error) {
+func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
        // See $GOROOT/include/ar.h.
        hdr := make([]byte, 16+12+6+6+8+10+2)
-       _, err = io.ReadFull(buf, hdr)
+       _, err = io.ReadFull(r, hdr)
        if err != nil {
                return
        }
@@ -36,33 +35,14 @@ func readGopackHeader(buf *bufio.Reader) (name string, size int, err error) {
        return
 }
 
-type dataReader struct {
-       *bufio.Reader
-       io.Closer
-}
-
-// ExportData returns a readCloser positioned at the beginning of the
-// export data section of the given object/archive file, or an error.
-// It is the caller's responsibility to close the readCloser.
+// FindGcExportData positions the reader r at the beginning of the
+// export data section of an underlying GC-created object/archive
+// file by reading from it. The reader must be positioned at the
+// start of the file before calling this function.
 //
-func ExportData(filename string) (rc io.ReadCloser, err error) {
-       file, err := os.Open(filename)
-       if err != nil {
-               return
-       }
-
-       defer func() {
-               if err != nil {
-                       file.Close()
-                       // Add file name to error.
-                       err = fmt.Errorf("reading export data: %s: %v", filename, err)
-               }
-       }()
-
-       buf := bufio.NewReader(file)
-
+func FindGcExportData(r *bufio.Reader) (err error) {
        // Read first line to make sure this is an object file.
-       line, err := buf.ReadSlice('\n')
+       line, err := r.ReadSlice('\n')
        if err != nil {
                return
        }
@@ -74,7 +54,7 @@ func ExportData(filename string) (rc io.ReadCloser, err error) {
 
                // First entry should be __.SYMDEF.
                // Read and discard.
-               if name, size, err = readGopackHeader(buf); err != nil {
+               if name, size, err = readGopackHeader(r); err != nil {
                        return
                }
                if name != "__.SYMDEF" {
@@ -88,15 +68,14 @@ func ExportData(filename string) (rc io.ReadCloser, err error) {
                        if n > block {
                                n = block
                        }
-                       _, err = io.ReadFull(buf, tmp[:n])
-                       if err != nil {
+                       if _, err = io.ReadFull(r, tmp[:n]); err != nil {
                                return
                        }
                        size -= n
                }
 
                // Second entry should be __.PKGDEF.
-               if name, size, err = readGopackHeader(buf); err != nil {
+               if name, size, err = readGopackHeader(r); err != nil {
                        return
                }
                if name != "__.PKGDEF" {
@@ -106,8 +85,7 @@ func ExportData(filename string) (rc io.ReadCloser, err error) {
 
                // Read first line of __.PKGDEF data, so that line
                // is once again the first line of the input.
-               line, err = buf.ReadSlice('\n')
-               if err != nil {
+               if line, err = r.ReadSlice('\n'); err != nil {
                        return
                }
        }
@@ -122,12 +100,10 @@ func ExportData(filename string) (rc io.ReadCloser, err error) {
        // Skip over object header to export data.
        // Begins after first line with $$.
        for line[0] != '$' {
-               line, err = buf.ReadSlice('\n')
-               if err != nil {
+               if line, err = r.ReadSlice('\n'); err != nil {
                        return
                }
        }
 
-       rc = &dataReader{buf, file}
        return
 }
index cb996f28055531868b516773db13ad70c7329638..07ab087abf8b3fa096e8e0a30df36b9587456d7a 100644 (file)
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// This file implements an ast.Importer for gc generated object files.
+// This file implements an ast.Importer for gc-generated object files.
 // TODO(gri) Eventually move this into a separate package outside types.
 
 package types
 
 import (
+       "bufio"
        "errors"
        "fmt"
        "go/ast"
@@ -24,41 +25,40 @@ import (
 
 const trace = false // set to true for debugging
 
-var (
-       pkgExts = [...]string{".a", ".5", ".6", ".8"}
-)
+var pkgExts = [...]string{".a", ".5", ".6", ".8"}
 
-// findPkg returns the filename and package id for an import path.
+// FindPkg returns the filename and unique package id for an import
+// path based on package information provided by build.Import (using
+// the build.Default build.Context).
 // If no file was found, an empty filename is returned.
-func findPkg(path string) (filename, id string) {
+//
+func FindPkg(path, srcDir string) (filename, id string) {
        if len(path) == 0 {
                return
        }
 
        id = path
        var noext string
-       switch path[0] {
+       switch {
        default:
                // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
-               bp, _ := build.Import(path, "", build.FindOnly)
+               bp, _ := build.Import(path, srcDir, build.FindOnly)
                if bp.PkgObj == "" {
                        return
                }
                noext = bp.PkgObj
                if strings.HasSuffix(noext, ".a") {
-                       noext = noext[:len(noext)-2]
+                       noext = noext[:len(noext)-len(".a")]
                }
 
-       case '.':
+       case build.IsLocalImport(path):
                // "./x" -> "/this/directory/x.ext", "/this/directory/x"
-               cwd, err := os.Getwd()
-               if err != nil {
-                       return
-               }
-               noext = filepath.Join(cwd, path)
+               noext = filepath.Join(srcDir, path)
                id = noext
 
-       case '/':
+       case filepath.IsAbs(path):
+               // for completeness only - go/build.Import
+               // does not support absolute imports
                // "/x" -> "/x.ext", "/x"
                noext = path
        }
@@ -75,6 +75,89 @@ func findPkg(path string) (filename, id string) {
        return
 }
 
+// GcImportData imports a package by reading the gc-generated export data,
+// adds the corresponding package object to the imports map indexed by id,
+// and returns the object.
+//
+// The imports map must contains all packages already imported, and no map
+// entry with id as the key must be present. The data reader position must
+// be the beginning of the export data section. The filename is only used
+// in error messages.
+//
+func GcImportData(imports map[string]*ast.Object, filename, id string, data *bufio.Reader) (pkg *ast.Object, err error) {
+       if trace {
+               fmt.Printf("importing %s (%s)\n", id, filename)
+       }
+
+       if imports[id] != nil {
+               panic(fmt.Sprintf("package %s already imported", id))
+       }
+
+       // support for gcParser error handling
+       defer func() {
+               if r := recover(); r != nil {
+                       err = r.(importError) // will re-panic if r is not an importError
+               }
+       }()
+
+       var p gcParser
+       p.init(filename, id, data, imports)
+       pkg = p.parseExport()
+
+       return
+}
+
+// GcImport imports a gc-generated package given its import path, adds the
+// corresponding package object to the imports map, and returns the object.
+// Local import paths are interpreted relative to the current working directory.
+// The imports map must contains all packages already imported.
+// GcImport satisfies the ast.Importer signature.
+//
+func GcImport(imports map[string]*ast.Object, path string) (pkg *ast.Object, err error) {
+       if path == "unsafe" {
+               return Unsafe, nil
+       }
+
+       srcDir, err := os.Getwd()
+       if err != nil {
+               return
+       }
+       filename, id := FindPkg(path, srcDir)
+       if filename == "" {
+               err = errors.New("can't find import: " + id)
+               return
+       }
+
+       if pkg = imports[id]; pkg != nil {
+               return // package was imported before
+       }
+
+       // open file
+       f, err := os.Open(filename)
+       if err != nil {
+               return
+       }
+       defer func() {
+               f.Close()
+               if err != nil {
+                       // Add file name to error.
+                       err = fmt.Errorf("reading export data: %s: %v", filename, err)
+               }
+       }()
+
+       buf := bufio.NewReader(f)
+       if err = FindGcExportData(buf); err != nil {
+               return
+       }
+
+       pkg, err = GcImportData(imports, filename, id, buf)
+
+       return
+}
+
+// ----------------------------------------------------------------------------
+// gcParser
+
 // gcParser parses the exports inside a gc compiler-produced
 // object/archive file and populates its scope with the results.
 type gcParser struct {
@@ -109,47 +192,6 @@ func (p *gcParser) next() {
        }
 }
 
-// GcImporter implements the ast.Importer signature.
-func GcImporter(imports map[string]*ast.Object, path string) (pkg *ast.Object, err error) {
-       if path == "unsafe" {
-               return Unsafe, nil
-       }
-
-       defer func() {
-               if r := recover(); r != nil {
-                       err = r.(importError) // will re-panic if r is not an importError
-                       if trace {
-                               panic(err) // force a stack trace
-                       }
-               }
-       }()
-
-       filename, id := findPkg(path)
-       if filename == "" {
-               err = errors.New("can't find import: " + id)
-               return
-       }
-
-       if pkg = imports[id]; pkg != nil {
-               return // package was imported before
-       }
-
-       buf, err := ExportData(filename)
-       if err != nil {
-               return
-       }
-       defer buf.Close()
-
-       if trace {
-               fmt.Printf("importing %s (%s)\n", id, filename)
-       }
-
-       var p gcParser
-       p.init(filename, id, buf, imports)
-       pkg = p.parseExport()
-       return
-}
-
 // Declare inserts a named object of the given kind in scope.
 func (p *gcParser) declare(scope *ast.Scope, kind ast.ObjKind, name string) *ast.Object {
        // the object may have been imported before - if it exists
@@ -707,7 +749,6 @@ func (p *gcParser) parseConstDecl() {
                p.next()
                typ = String.Underlying
        default:
-               println(p.tok)
                p.errorf("expected literal got %s", scanner.TokenString(p.tok))
        }
        if obj.Type == nil {
index c229b50113db32b43a9c4914c7c2ba8fae41f7dd..20247b0dc44c9dfceda55e8f9dba308461d65e3e 100644 (file)
@@ -17,23 +17,23 @@ import (
        "time"
 )
 
-var gcName, gcPath string // compiler name and path
+var gcPath string // Go compiler path
 
 func init() {
        // determine compiler
+       var gc string
        switch runtime.GOARCH {
        case "386":
-               gcName = "8g"
+               gc = "8g"
        case "amd64":
-               gcName = "6g"
+               gc = "6g"
        case "arm":
-               gcName = "5g"
+               gc = "5g"
        default:
-               gcName = "unknown-GOARCH-compiler"
-               gcPath = gcName
+               gcPath = "unknown-GOARCH-compiler"
                return
        }
-       gcPath = filepath.Join(build.ToolDir, gcName)
+       gcPath = filepath.Join(build.ToolDir, gc)
 }
 
 func compile(t *testing.T, dirname, filename string) {
@@ -41,7 +41,7 @@ func compile(t *testing.T, dirname, filename string) {
        cmd.Dir = dirname
        out, err := cmd.CombinedOutput()
        if err != nil {
-               t.Errorf("%s %s failed: %s", gcName, filename, err)
+               t.Errorf("%s %s failed: %s", gcPath, filename, err)
                return
        }
        t.Logf("%s", string(out))
@@ -52,7 +52,7 @@ func compile(t *testing.T, dirname, filename string) {
 var imports = make(map[string]*ast.Object)
 
 func testPath(t *testing.T, path string) bool {
-       _, err := GcImporter(imports, path)
+       _, err := GcImport(imports, path)
        if err != nil {
                t.Errorf("testPath(%s): %s", path, err)
                return false