mirror of
https://github.com/golang/go
synced 2024-11-20 02:04:39 -07:00
go/types: New Go type hierarchy implementation for AST.
This CL defines a new, more Go-like representation of Go types (different structs for different types as opposed to a single Type node). It also implements an ast.Importer for object/archive files generated by the gc compiler tool chain. Besides the individual type structs, the main difference is the handling of named types: In the old world, a named type had a non-nil *Object pointer but otherwise looked no different from other types. In this new model, named types have their own representation types.Name. As a result, resolving cycles is a bit simpler during construction, at the cost of having to deal with types.Name nodes explicitly later. It remains to be seen if this is a good approach. Nevertheless, code involving types reads more nicely and benefits from full type checking. Also, the representation seems to more closely match the spec wording. Credits: The original version of the gc importer was written by Evan Shaw (chickencha@gmail.com). The new version in this CL is based largely on Evan's original code but contains bug fixes, a few simplifications, some restructuring, and was adjusted to use the new type hierarchy. I have added a comprehensive test that imports all packages found under $GOROOT/pkg (with a 3s time-out to limit the run-time of the test). Run gotest -v for details. The original version of ExportData (exportdata.go) was written by Russ Cox (rsc@golang.org). The current version is returning the internal buffer positioned at the beginning of the export data instead of printing the export data to stdout. With the new types package, the existing in-progress typechecker package is deprecated. I will delete it once all functionality has been brought over. R=eds, rog, rsc CC=golang-dev https://golang.org/cl/4314054
This commit is contained in:
parent
70bf4215e9
commit
a87382e7b3
@ -90,6 +90,7 @@ DIRS=\
|
||||
go/scanner\
|
||||
go/token\
|
||||
go/typechecker\
|
||||
go/types\
|
||||
gob\
|
||||
hash\
|
||||
hash/adler32\
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// INCOMPLETE PACKAGE.
|
||||
// DEPRECATED PACKAGE - SEE go/types INSTEAD.
|
||||
// This package implements typechecking of a Go AST.
|
||||
// The result of the typecheck is an augmented AST
|
||||
// with object and type information for each identifier.
|
||||
|
15
src/pkg/go/types/Makefile
Normal file
15
src/pkg/go/types/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright 2010 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
include ../../../Make.inc
|
||||
|
||||
TARG=go/types
|
||||
GOFILES=\
|
||||
const.go\
|
||||
exportdata.go\
|
||||
gcimporter.go\
|
||||
types.go\
|
||||
universe.go\
|
||||
|
||||
include ../../../Make.pkg
|
347
src/pkg/go/types/const.go
Normal file
347
src/pkg/go/types/const.go
Normal file
@ -0,0 +1,347 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements operations on ideal constants.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"big"
|
||||
"go/token"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
// TODO(gri) Consider changing the API so Const is an interface
|
||||
// and operations on consts don't have to type switch.
|
||||
|
||||
// A Const implements an ideal constant Value.
|
||||
// The zero value z for a Const is not a valid constant value.
|
||||
type Const struct {
|
||||
// representation of constant values:
|
||||
// ideal bool -> bool
|
||||
// ideal int -> *big.Int
|
||||
// ideal float -> *big.Rat
|
||||
// ideal complex -> cmplx
|
||||
// ideal string -> string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
|
||||
// Representation of complex values.
|
||||
type cmplx struct {
|
||||
re, im *big.Rat
|
||||
}
|
||||
|
||||
|
||||
func assert(cond bool) {
|
||||
if !cond {
|
||||
panic("go/types internal error: assertion failed")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MakeConst makes an ideal constant from a literal
|
||||
// token and the corresponding literal string.
|
||||
func MakeConst(tok token.Token, lit string) Const {
|
||||
switch tok {
|
||||
case token.INT:
|
||||
var x big.Int
|
||||
_, ok := x.SetString(lit, 0)
|
||||
assert(ok)
|
||||
return Const{&x}
|
||||
case token.FLOAT:
|
||||
var y big.Rat
|
||||
_, ok := y.SetString(lit)
|
||||
assert(ok)
|
||||
return Const{&y}
|
||||
case token.IMAG:
|
||||
assert(lit[len(lit)-1] == 'i')
|
||||
var im big.Rat
|
||||
_, ok := im.SetString(lit[0 : len(lit)-1])
|
||||
assert(ok)
|
||||
return Const{cmplx{big.NewRat(0, 1), &im}}
|
||||
case token.CHAR:
|
||||
assert(lit[0] == '\'' && lit[len(lit)-1] == '\'')
|
||||
code, _, _, err := strconv.UnquoteChar(lit[1:len(lit)-1], '\'')
|
||||
assert(err == nil)
|
||||
return Const{big.NewInt(int64(code))}
|
||||
case token.STRING:
|
||||
s, err := strconv.Unquote(lit)
|
||||
assert(err == nil)
|
||||
return Const{s}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
|
||||
// MakeZero returns the zero constant for the given type.
|
||||
func MakeZero(typ *Type) Const {
|
||||
// TODO(gri) fix this
|
||||
return Const{0}
|
||||
}
|
||||
|
||||
|
||||
// Match attempts to match the internal constant representations of x and y.
|
||||
// If the attempt is successful, the result is the values of x and y,
|
||||
// if necessary converted to have the same internal representation; otherwise
|
||||
// the results are invalid.
|
||||
func (x Const) Match(y Const) (u, v Const) {
|
||||
switch a := x.val.(type) {
|
||||
case bool:
|
||||
if _, ok := y.val.(bool); ok {
|
||||
u, v = x, y
|
||||
}
|
||||
case *big.Int:
|
||||
switch y.val.(type) {
|
||||
case *big.Int:
|
||||
u, v = x, y
|
||||
case *big.Rat:
|
||||
var z big.Rat
|
||||
z.SetInt(a)
|
||||
u, v = Const{&z}, y
|
||||
case cmplx:
|
||||
var z big.Rat
|
||||
z.SetInt(a)
|
||||
u, v = Const{cmplx{&z, big.NewRat(0, 1)}}, y
|
||||
}
|
||||
case *big.Rat:
|
||||
switch y.val.(type) {
|
||||
case *big.Int:
|
||||
v, u = y.Match(x)
|
||||
case *big.Rat:
|
||||
u, v = x, y
|
||||
case cmplx:
|
||||
u, v = Const{cmplx{a, big.NewRat(0, 0)}}, y
|
||||
}
|
||||
case cmplx:
|
||||
switch y.val.(type) {
|
||||
case *big.Int, *big.Rat:
|
||||
v, u = y.Match(x)
|
||||
case cmplx:
|
||||
u, v = x, y
|
||||
}
|
||||
case string:
|
||||
if _, ok := y.val.(string); ok {
|
||||
u, v = x, y
|
||||
}
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Convert attempts to convert the constant x to a given type.
|
||||
// If the attempt is successful, the result is the new constant;
|
||||
// otherwise the result is invalid.
|
||||
func (x Const) Convert(typ *Type) Const {
|
||||
// TODO(gri) implement this
|
||||
switch x := x.val.(type) {
|
||||
case bool:
|
||||
case *big.Int:
|
||||
case *big.Rat:
|
||||
case cmplx:
|
||||
case string:
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
|
||||
func (x Const) String() string {
|
||||
switch x := x.val.(type) {
|
||||
case bool:
|
||||
if x {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
case *big.Int:
|
||||
return x.String()
|
||||
case *big.Rat:
|
||||
return x.FloatString(10) // 10 digits of precision after decimal point seems fine
|
||||
case cmplx:
|
||||
// TODO(gri) don't print 0 components
|
||||
return x.re.FloatString(10) + " + " + x.im.FloatString(10) + "i"
|
||||
case string:
|
||||
return x
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
|
||||
func (x Const) UnaryOp(op token.Token) Const {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
|
||||
func (x Const) BinaryOp(op token.Token, y Const) Const {
|
||||
var z interface{}
|
||||
switch x := x.val.(type) {
|
||||
case bool:
|
||||
z = binaryBoolOp(x, op, y.val.(bool))
|
||||
case *big.Int:
|
||||
z = binaryIntOp(x, op, y.val.(*big.Int))
|
||||
case *big.Rat:
|
||||
z = binaryFloatOp(x, op, y.val.(*big.Rat))
|
||||
case cmplx:
|
||||
z = binaryCmplxOp(x, op, y.val.(cmplx))
|
||||
case string:
|
||||
z = binaryStringOp(x, op, y.val.(string))
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
return Const{z}
|
||||
}
|
||||
|
||||
|
||||
func binaryBoolOp(x bool, op token.Token, y bool) interface{} {
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return x == y
|
||||
case token.NEQ:
|
||||
return x != y
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
|
||||
func binaryIntOp(x *big.Int, op token.Token, y *big.Int) interface{} {
|
||||
var z big.Int
|
||||
switch op {
|
||||
case token.ADD:
|
||||
return z.Add(x, y)
|
||||
case token.SUB:
|
||||
return z.Sub(x, y)
|
||||
case token.MUL:
|
||||
return z.Mul(x, y)
|
||||
case token.QUO:
|
||||
return z.Quo(x, y)
|
||||
case token.REM:
|
||||
return z.Rem(x, y)
|
||||
case token.AND:
|
||||
return z.And(x, y)
|
||||
case token.OR:
|
||||
return z.Or(x, y)
|
||||
case token.XOR:
|
||||
return z.Xor(x, y)
|
||||
case token.AND_NOT:
|
||||
return z.AndNot(x, y)
|
||||
case token.SHL:
|
||||
panic("unimplemented")
|
||||
case token.SHR:
|
||||
panic("unimplemented")
|
||||
case token.EQL:
|
||||
return x.Cmp(y) == 0
|
||||
case token.NEQ:
|
||||
return x.Cmp(y) != 0
|
||||
case token.LSS:
|
||||
return x.Cmp(y) < 0
|
||||
case token.LEQ:
|
||||
return x.Cmp(y) <= 0
|
||||
case token.GTR:
|
||||
return x.Cmp(y) > 0
|
||||
case token.GEQ:
|
||||
return x.Cmp(y) >= 0
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
|
||||
func binaryFloatOp(x *big.Rat, op token.Token, y *big.Rat) interface{} {
|
||||
var z big.Rat
|
||||
switch op {
|
||||
case token.ADD:
|
||||
return z.Add(x, y)
|
||||
case token.SUB:
|
||||
return z.Sub(x, y)
|
||||
case token.MUL:
|
||||
return z.Mul(x, y)
|
||||
case token.QUO:
|
||||
return z.Quo(x, y)
|
||||
case token.EQL:
|
||||
return x.Cmp(y) == 0
|
||||
case token.NEQ:
|
||||
return x.Cmp(y) != 0
|
||||
case token.LSS:
|
||||
return x.Cmp(y) < 0
|
||||
case token.LEQ:
|
||||
return x.Cmp(y) <= 0
|
||||
case token.GTR:
|
||||
return x.Cmp(y) > 0
|
||||
case token.GEQ:
|
||||
return x.Cmp(y) >= 0
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
|
||||
func binaryCmplxOp(x cmplx, op token.Token, y cmplx) interface{} {
|
||||
a, b := x.re, x.im
|
||||
c, d := y.re, y.im
|
||||
switch op {
|
||||
case token.ADD:
|
||||
// (a+c) + i(b+d)
|
||||
var re, im big.Rat
|
||||
re.Add(a, c)
|
||||
im.Add(b, d)
|
||||
return cmplx{&re, &im}
|
||||
case token.SUB:
|
||||
// (a-c) + i(b-d)
|
||||
var re, im big.Rat
|
||||
re.Sub(a, c)
|
||||
im.Sub(b, d)
|
||||
return cmplx{&re, &im}
|
||||
case token.MUL:
|
||||
// (ac-bd) + i(bc+ad)
|
||||
var ac, bd, bc, ad big.Rat
|
||||
ac.Mul(a, c)
|
||||
bd.Mul(b, d)
|
||||
bc.Mul(b, c)
|
||||
ad.Mul(a, d)
|
||||
var re, im big.Rat
|
||||
re.Sub(&ac, &bd)
|
||||
im.Add(&bc, &ad)
|
||||
return cmplx{&re, &im}
|
||||
case token.QUO:
|
||||
// (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
|
||||
var ac, bd, bc, ad, s big.Rat
|
||||
ac.Mul(a, c)
|
||||
bd.Mul(b, d)
|
||||
bc.Mul(b, c)
|
||||
ad.Mul(a, d)
|
||||
s.Add(c.Mul(c, c), d.Mul(d, d))
|
||||
var re, im big.Rat
|
||||
re.Add(&ac, &bd)
|
||||
re.Quo(&re, &s)
|
||||
im.Sub(&bc, &ad)
|
||||
im.Quo(&im, &s)
|
||||
return cmplx{&re, &im}
|
||||
case token.EQL:
|
||||
return a.Cmp(c) == 0 && b.Cmp(d) == 0
|
||||
case token.NEQ:
|
||||
return a.Cmp(c) != 0 || b.Cmp(d) != 0
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
|
||||
func binaryStringOp(x string, op token.Token, y string) interface{} {
|
||||
switch op {
|
||||
case token.ADD:
|
||||
return x + y
|
||||
case token.EQL:
|
||||
return x == y
|
||||
case token.NEQ:
|
||||
return x != y
|
||||
case token.LSS:
|
||||
return x < y
|
||||
case token.LEQ:
|
||||
return x <= y
|
||||
case token.GTR:
|
||||
return x > y
|
||||
case token.GEQ:
|
||||
return x >= y
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
135
src/pkg/go/types/exportdata.go
Normal file
135
src/pkg/go/types/exportdata.go
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements ExportData.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) {
|
||||
// See $GOROOT/include/ar.h.
|
||||
hdr := make([]byte, 64+12+6+6+8+10+2)
|
||||
_, err = io.ReadFull(buf, hdr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if trace {
|
||||
fmt.Printf("header: %s", hdr)
|
||||
}
|
||||
s := strings.TrimSpace(string(hdr[64+12+6+6+8:][:10]))
|
||||
size, err = strconv.Atoi(s)
|
||||
if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
|
||||
err = os.ErrorString("invalid archive header")
|
||||
return
|
||||
}
|
||||
name = strings.TrimSpace(string(hdr[:64]))
|
||||
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.
|
||||
//
|
||||
func ExportData(filename string) (rc io.ReadCloser, err os.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)
|
||||
|
||||
// Read first line to make sure this is an object file.
|
||||
line, err := buf.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if string(line) == "!<arch>\n" {
|
||||
// Archive file. Scan to __.PKGDEF, which should
|
||||
// be second archive entry.
|
||||
var name string
|
||||
var size int
|
||||
|
||||
// First entry should be __.SYMDEF.
|
||||
// Read and discard.
|
||||
if name, size, err = readGopackHeader(buf); err != nil {
|
||||
return
|
||||
}
|
||||
if name != "__.SYMDEF" {
|
||||
err = os.ErrorString("go archive does not begin with __.SYMDEF")
|
||||
return
|
||||
}
|
||||
const block = 4096
|
||||
tmp := make([]byte, block)
|
||||
for size > 0 {
|
||||
n := size
|
||||
if n > block {
|
||||
n = block
|
||||
}
|
||||
_, err = io.ReadFull(buf, tmp[:n])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
size -= n
|
||||
}
|
||||
|
||||
// Second entry should be __.PKGDEF.
|
||||
if name, size, err = readGopackHeader(buf); err != nil {
|
||||
return
|
||||
}
|
||||
if name != "__.PKGDEF" {
|
||||
err = os.ErrorString("go archive is missing __.PKGDEF")
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Now at __.PKGDEF in archive or still at beginning of file.
|
||||
// Either way, line should begin with "go object ".
|
||||
if !strings.HasPrefix(string(line), "go object ") {
|
||||
err = os.ErrorString("not a go object file")
|
||||
return
|
||||
}
|
||||
|
||||
// Skip over object header to export data.
|
||||
// Begins after first line with $$.
|
||||
for line[0] != '$' {
|
||||
line, err = buf.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rc = &dataReader{buf, file}
|
||||
return
|
||||
}
|
786
src/pkg/go/types/gcimporter.go
Normal file
786
src/pkg/go/types/gcimporter.go
Normal file
@ -0,0 +1,786 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// 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.
|
||||
// TODO(gri) Eventually move this into a separate package outside types.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"big"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"scanner"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
const trace = false // set to true for debugging
|
||||
|
||||
var (
|
||||
pkgRoot = filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH)
|
||||
pkgExts = [...]string{".a", ".5", ".6", ".8"}
|
||||
)
|
||||
|
||||
|
||||
// findPkg returns the filename and package id for an import path.
|
||||
// If no file was found, an empty filename is returned.
|
||||
func findPkg(path string) (filename, id string) {
|
||||
if len(path) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
id = path
|
||||
var noext string
|
||||
switch path[0] {
|
||||
default:
|
||||
// "x" -> "$GOROOT/pkg/$GOOS_$GOARCH/x.ext", "x"
|
||||
noext = filepath.Join(pkgRoot, path)
|
||||
|
||||
case '.':
|
||||
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
noext = filepath.Join(cwd, path)
|
||||
id = noext
|
||||
|
||||
case '/':
|
||||
// "/x" -> "/x.ext", "/x"
|
||||
noext = path
|
||||
}
|
||||
|
||||
// try extensions
|
||||
for _, ext := range pkgExts {
|
||||
filename = noext + ext
|
||||
if f, err := os.Stat(filename); err == nil && f.IsRegular() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
filename = "" // not found
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// gcParser parses the exports inside a gc compiler-produced
|
||||
// object/archive file and populates its scope with the results.
|
||||
type gcParser struct {
|
||||
scanner scanner.Scanner
|
||||
tok int // current token
|
||||
lit string // literal string; only valid for Ident, Int, String tokens
|
||||
id string // package id of imported package
|
||||
scope *ast.Scope // scope of imported package; alias for deps[id]
|
||||
deps map[string]*ast.Scope // package id -> package scope
|
||||
}
|
||||
|
||||
|
||||
func (p *gcParser) init(filename, id string, src io.Reader) {
|
||||
p.scanner.Init(src)
|
||||
p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
|
||||
p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
|
||||
p.scanner.Whitespace = 1<<'\t' | 1<<' '
|
||||
p.scanner.Filename = filename // for good error messages
|
||||
p.next()
|
||||
p.id = id
|
||||
p.scope = ast.NewScope(nil)
|
||||
p.deps = map[string]*ast.Scope{"unsafe": Unsafe, id: p.scope}
|
||||
}
|
||||
|
||||
|
||||
func (p *gcParser) next() {
|
||||
p.tok = p.scanner.Scan()
|
||||
switch p.tok {
|
||||
case scanner.Ident, scanner.Int, scanner.String:
|
||||
p.lit = p.scanner.TokenText()
|
||||
default:
|
||||
p.lit = ""
|
||||
}
|
||||
if trace {
|
||||
fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// GcImporter implements the ast.Importer signature.
|
||||
func GcImporter(path string) (name string, scope *ast.Scope, err os.Error) {
|
||||
if path == "unsafe" {
|
||||
return path, 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 = os.ErrorString("can't find import: " + id)
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := ExportData(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer buf.Close()
|
||||
|
||||
if trace {
|
||||
fmt.Printf("importing %s\n", filename)
|
||||
}
|
||||
|
||||
var p gcParser
|
||||
p.init(filename, id, buf)
|
||||
name, scope = p.parseExport()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Error handling
|
||||
|
||||
// Internal errors are boxed as importErrors.
|
||||
type importError struct {
|
||||
pos scanner.Position
|
||||
err os.Error
|
||||
}
|
||||
|
||||
|
||||
func (e importError) String() string {
|
||||
return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
|
||||
}
|
||||
|
||||
|
||||
func (p *gcParser) error(err interface{}) {
|
||||
if s, ok := err.(string); ok {
|
||||
err = os.ErrorString(s)
|
||||
}
|
||||
// panic with a runtime.Error if err is not an os.Error
|
||||
panic(importError{p.scanner.Pos(), err.(os.Error)})
|
||||
}
|
||||
|
||||
|
||||
func (p *gcParser) errorf(format string, args ...interface{}) {
|
||||
p.error(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
|
||||
func (p *gcParser) expect(tok int) string {
|
||||
lit := p.lit
|
||||
if p.tok != tok {
|
||||
p.errorf("expected %q, got %q (%q)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
|
||||
}
|
||||
p.next()
|
||||
return lit
|
||||
}
|
||||
|
||||
|
||||
func (p *gcParser) expectSpecial(tok string) {
|
||||
sep := 'x' // not white space
|
||||
i := 0
|
||||
for i < len(tok) && p.tok == int(tok[i]) && sep > ' ' {
|
||||
sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
|
||||
p.next()
|
||||
i++
|
||||
}
|
||||
if i < len(tok) {
|
||||
p.errorf("expected %q, got %q", tok, tok[0:i])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (p *gcParser) expectKeyword(keyword string) {
|
||||
lit := p.expect(scanner.Ident)
|
||||
if lit != keyword {
|
||||
p.errorf("expected keyword %s, got %q", keyword, lit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Import declarations
|
||||
|
||||
// ImportPath = string_lit .
|
||||
//
|
||||
func (p *gcParser) parsePkgId() *ast.Scope {
|
||||
id, err := strconv.Unquote(p.expect(scanner.String))
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
}
|
||||
|
||||
scope := p.scope // id == "" stands for the imported package id
|
||||
if id != "" {
|
||||
if scope = p.deps[id]; scope == nil {
|
||||
scope = ast.NewScope(nil)
|
||||
p.deps[id] = scope
|
||||
}
|
||||
}
|
||||
|
||||
return scope
|
||||
}
|
||||
|
||||
|
||||
// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
|
||||
func (p *gcParser) parseDotIdent() string {
|
||||
ident := ""
|
||||
if p.tok != scanner.Int {
|
||||
sep := 'x' // not white space
|
||||
for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
|
||||
ident += p.lit
|
||||
sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
if ident == "" {
|
||||
p.expect(scanner.Ident) // use expect() for error handling
|
||||
}
|
||||
return ident
|
||||
}
|
||||
|
||||
|
||||
// ExportedName = ImportPath "." dotIdentifier .
|
||||
//
|
||||
func (p *gcParser) parseExportedName(kind ast.ObjKind) *ast.Object {
|
||||
scope := p.parsePkgId()
|
||||
p.expect('.')
|
||||
name := p.parseDotIdent()
|
||||
|
||||
// a type may have been declared before - if it exists
|
||||
// already in the respective package scope, return that
|
||||
// type
|
||||
if kind == ast.Typ {
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
assert(obj.Kind == ast.Typ)
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
// any other object must be a newly declared object -
|
||||
// create it and insert it into the package scope
|
||||
obj := ast.NewObj(kind, name)
|
||||
if scope.Insert(obj) != nil {
|
||||
p.errorf("already declared: %s", obj.Name)
|
||||
}
|
||||
|
||||
// a new type object is a named type and may be referred
|
||||
// to before the underlying type is known - set it up
|
||||
if kind == ast.Typ {
|
||||
obj.Type = &Name{Obj: obj}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Types
|
||||
|
||||
// BasicType = identifier .
|
||||
//
|
||||
func (p *gcParser) parseBasicType() Type {
|
||||
obj := Universe.Lookup(p.expect(scanner.Ident))
|
||||
if obj == nil || obj.Kind != ast.Typ {
|
||||
p.errorf("not a basic type: %s", obj.Name)
|
||||
}
|
||||
return obj.Type.(Type)
|
||||
}
|
||||
|
||||
|
||||
// ArrayType = "[" int_lit "]" Type .
|
||||
//
|
||||
func (p *gcParser) parseArrayType() Type {
|
||||
// "[" already consumed and lookahead known not to be "]"
|
||||
lit := p.expect(scanner.Int)
|
||||
p.expect(']')
|
||||
elt := p.parseType()
|
||||
n, err := strconv.Atoui64(lit)
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
}
|
||||
return &Array{Len: n, Elt: elt}
|
||||
}
|
||||
|
||||
|
||||
// MapType = "map" "[" Type "]" Type .
|
||||
//
|
||||
func (p *gcParser) parseMapType() Type {
|
||||
p.expectKeyword("map")
|
||||
p.expect('[')
|
||||
key := p.parseType()
|
||||
p.expect(']')
|
||||
elt := p.parseType()
|
||||
return &Map{Key: key, Elt: elt}
|
||||
}
|
||||
|
||||
|
||||
// Name = identifier | "?" .
|
||||
//
|
||||
func (p *gcParser) parseName() (name string) {
|
||||
switch p.tok {
|
||||
case scanner.Ident:
|
||||
name = p.lit
|
||||
p.next()
|
||||
case '?':
|
||||
// anonymous
|
||||
p.next()
|
||||
default:
|
||||
p.error("name expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Field = Name Type [ ":" string_lit ] .
|
||||
//
|
||||
func (p *gcParser) parseField(scope *ast.Scope) {
|
||||
// TODO(gri) The code below is not correct for anonymous fields:
|
||||
// The name is the type name; it should not be empty.
|
||||
name := p.parseName()
|
||||
ftyp := p.parseType()
|
||||
if name == "" {
|
||||
// anonymous field - ftyp must be T or *T and T must be a type name
|
||||
ftyp = Deref(ftyp)
|
||||
if ftyp, ok := ftyp.(*Name); ok {
|
||||
name = ftyp.Obj.Name
|
||||
} else {
|
||||
p.errorf("anonymous field expected")
|
||||
}
|
||||
}
|
||||
if p.tok == ':' {
|
||||
p.next()
|
||||
tag := p.expect(scanner.String)
|
||||
_ = tag // TODO(gri) store tag somewhere
|
||||
}
|
||||
fld := ast.NewObj(ast.Var, name)
|
||||
fld.Type = ftyp
|
||||
scope.Insert(fld)
|
||||
}
|
||||
|
||||
|
||||
// StructType = "struct" "{" [ FieldList ] "}" .
|
||||
// FieldList = Field { ";" Field } .
|
||||
//
|
||||
func (p *gcParser) parseStructType() Type {
|
||||
p.expectKeyword("struct")
|
||||
p.expect('{')
|
||||
scope := ast.NewScope(nil)
|
||||
if p.tok != '}' {
|
||||
p.parseField(scope)
|
||||
for p.tok == ';' {
|
||||
p.next()
|
||||
p.parseField(scope)
|
||||
}
|
||||
}
|
||||
p.expect('}')
|
||||
return &Struct{}
|
||||
}
|
||||
|
||||
|
||||
// Parameter = ( identifier | "?" ) [ "..." ] Type .
|
||||
//
|
||||
func (p *gcParser) parseParameter(scope *ast.Scope, isVariadic *bool) {
|
||||
name := p.parseName()
|
||||
if name == "" {
|
||||
name = "_" // cannot access unnamed identifiers
|
||||
}
|
||||
if isVariadic != nil {
|
||||
if *isVariadic {
|
||||
p.error("... not on final argument")
|
||||
}
|
||||
if p.tok == '.' {
|
||||
p.expectSpecial("...")
|
||||
*isVariadic = true
|
||||
}
|
||||
}
|
||||
ptyp := p.parseType()
|
||||
par := ast.NewObj(ast.Var, name)
|
||||
par.Type = ptyp
|
||||
scope.Insert(par)
|
||||
}
|
||||
|
||||
|
||||
// Parameters = "(" [ ParameterList ] ")" .
|
||||
// ParameterList = { Parameter "," } Parameter .
|
||||
//
|
||||
func (p *gcParser) parseParameters(scope *ast.Scope, isVariadic *bool) {
|
||||
p.expect('(')
|
||||
if p.tok != ')' {
|
||||
p.parseParameter(scope, isVariadic)
|
||||
for p.tok == ',' {
|
||||
p.next()
|
||||
p.parseParameter(scope, isVariadic)
|
||||
}
|
||||
}
|
||||
p.expect(')')
|
||||
}
|
||||
|
||||
|
||||
// Signature = Parameters [ Result ] .
|
||||
// Result = Type | Parameters .
|
||||
//
|
||||
func (p *gcParser) parseSignature(scope *ast.Scope, isVariadic *bool) {
|
||||
p.parseParameters(scope, isVariadic)
|
||||
|
||||
// optional result type
|
||||
switch p.tok {
|
||||
case scanner.Ident, scanner.String, '[', '*', '<':
|
||||
// single, unnamed result
|
||||
result := ast.NewObj(ast.Var, "_")
|
||||
result.Type = p.parseType()
|
||||
scope.Insert(result)
|
||||
case '(':
|
||||
// named or multiple result(s)
|
||||
p.parseParameters(scope, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FuncType = "func" Signature .
|
||||
//
|
||||
func (p *gcParser) parseFuncType() Type {
|
||||
// "func" already consumed
|
||||
scope := ast.NewScope(nil)
|
||||
isVariadic := false
|
||||
p.parseSignature(scope, &isVariadic)
|
||||
return &Func{IsVariadic: isVariadic}
|
||||
}
|
||||
|
||||
|
||||
// MethodSpec = identifier Signature .
|
||||
//
|
||||
func (p *gcParser) parseMethodSpec(scope *ast.Scope) {
|
||||
p.expect(scanner.Ident)
|
||||
isVariadic := false
|
||||
p.parseSignature(scope, &isVariadic)
|
||||
}
|
||||
|
||||
|
||||
// InterfaceType = "interface" "{" [ MethodList ] "}" .
|
||||
// MethodList = MethodSpec { ";" MethodSpec } .
|
||||
//
|
||||
func (p *gcParser) parseInterfaceType() Type {
|
||||
p.expectKeyword("interface")
|
||||
p.expect('{')
|
||||
scope := ast.NewScope(nil)
|
||||
if p.tok != '}' {
|
||||
p.parseMethodSpec(scope)
|
||||
for p.tok == ';' {
|
||||
p.next()
|
||||
p.parseMethodSpec(scope)
|
||||
}
|
||||
}
|
||||
p.expect('}')
|
||||
return &Interface{}
|
||||
}
|
||||
|
||||
|
||||
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
|
||||
//
|
||||
func (p *gcParser) parseChanType() Type {
|
||||
dir := ast.SEND | ast.RECV
|
||||
if p.tok == scanner.Ident {
|
||||
p.expectKeyword("chan")
|
||||
if p.tok == '<' {
|
||||
p.expectSpecial("<-")
|
||||
dir = ast.SEND
|
||||
}
|
||||
} else {
|
||||
p.expectSpecial("<-")
|
||||
p.expectKeyword("chan")
|
||||
dir = ast.RECV
|
||||
}
|
||||
elt := p.parseType()
|
||||
return &Chan{Dir: dir, Elt: elt}
|
||||
}
|
||||
|
||||
|
||||
// Type =
|
||||
// BasicType | TypeName | ArrayType | SliceType | StructType |
|
||||
// PointerType | FuncType | InterfaceType | MapType | ChanType |
|
||||
// "(" Type ")" .
|
||||
// BasicType = ident .
|
||||
// TypeName = ExportedName .
|
||||
// SliceType = "[" "]" Type .
|
||||
// PointerType = "*" Type .
|
||||
//
|
||||
func (p *gcParser) parseType() Type {
|
||||
switch p.tok {
|
||||
case scanner.Ident:
|
||||
switch p.lit {
|
||||
default:
|
||||
return p.parseBasicType()
|
||||
case "struct":
|
||||
return p.parseStructType()
|
||||
case "func":
|
||||
p.next() // parseFuncType assumes "func" is already consumed
|
||||
return p.parseFuncType()
|
||||
case "interface":
|
||||
return p.parseInterfaceType()
|
||||
case "map":
|
||||
return p.parseMapType()
|
||||
case "chan":
|
||||
return p.parseChanType()
|
||||
}
|
||||
case scanner.String:
|
||||
// TypeName
|
||||
return p.parseExportedName(ast.Typ).Type.(Type)
|
||||
case '[':
|
||||
p.next() // look ahead
|
||||
if p.tok == ']' {
|
||||
// SliceType
|
||||
p.next()
|
||||
return &Slice{Elt: p.parseType()}
|
||||
}
|
||||
return p.parseArrayType()
|
||||
case '*':
|
||||
// PointerType
|
||||
p.next()
|
||||
return &Pointer{Base: p.parseType()}
|
||||
case '<':
|
||||
return p.parseChanType()
|
||||
case '(':
|
||||
// "(" Type ")"
|
||||
p.next()
|
||||
typ := p.parseType()
|
||||
p.expect(')')
|
||||
return typ
|
||||
}
|
||||
p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Declarations
|
||||
|
||||
// ImportDecl = "import" identifier string_lit .
|
||||
//
|
||||
func (p *gcParser) parseImportDecl() {
|
||||
p.expectKeyword("import")
|
||||
// The identifier has no semantic meaning in the import data.
|
||||
// It exists so that error messages can print the real package
|
||||
// name: binary.ByteOrder instead of "encoding/binary".ByteOrder.
|
||||
// TODO(gri): Save package id -> package name mapping.
|
||||
p.expect(scanner.Ident)
|
||||
p.parsePkgId()
|
||||
}
|
||||
|
||||
|
||||
// int_lit = [ "+" | "-" ] { "0" ... "9" } .
|
||||
//
|
||||
func (p *gcParser) parseInt() (sign, val string) {
|
||||
switch p.tok {
|
||||
case '-':
|
||||
p.next()
|
||||
sign = "-"
|
||||
case '+':
|
||||
p.next()
|
||||
}
|
||||
val = p.expect(scanner.Int)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// number = int_lit [ "p" int_lit ] .
|
||||
//
|
||||
func (p *gcParser) parseNumber() Const {
|
||||
// mantissa
|
||||
sign, val := p.parseInt()
|
||||
mant, ok := new(big.Int).SetString(sign+val, 10)
|
||||
assert(ok)
|
||||
|
||||
if p.lit == "p" {
|
||||
// exponent (base 2)
|
||||
p.next()
|
||||
sign, val = p.parseInt()
|
||||
exp, err := strconv.Atoui(val)
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
}
|
||||
if sign == "-" {
|
||||
denom := big.NewInt(1)
|
||||
denom.Lsh(denom, exp)
|
||||
return Const{new(big.Rat).SetFrac(mant, denom)}
|
||||
}
|
||||
if exp > 0 {
|
||||
mant.Lsh(mant, exp)
|
||||
}
|
||||
return Const{new(big.Rat).SetInt(mant)}
|
||||
}
|
||||
|
||||
return Const{mant}
|
||||
}
|
||||
|
||||
|
||||
// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
|
||||
// Literal = bool_lit | int_lit | float_lit | complex_lit | string_lit .
|
||||
// bool_lit = "true" | "false" .
|
||||
// complex_lit = "(" float_lit "+" float_lit ")" .
|
||||
// string_lit = `"` { unicode_char } `"` .
|
||||
//
|
||||
func (p *gcParser) parseConstDecl() {
|
||||
p.expectKeyword("const")
|
||||
obj := p.parseExportedName(ast.Con)
|
||||
var x Const
|
||||
var typ Type
|
||||
if p.tok != '=' {
|
||||
obj.Type = p.parseType()
|
||||
}
|
||||
p.expect('=')
|
||||
switch p.tok {
|
||||
case scanner.Ident:
|
||||
// bool_lit
|
||||
if p.lit != "true" && p.lit != "false" {
|
||||
p.error("expected true or false")
|
||||
}
|
||||
x = Const{p.lit == "true"}
|
||||
typ = Bool.Underlying
|
||||
p.next()
|
||||
case '-', scanner.Int:
|
||||
// int_lit
|
||||
x = p.parseNumber()
|
||||
typ = Int.Underlying
|
||||
if _, ok := x.val.(*big.Rat); ok {
|
||||
typ = Float64.Underlying
|
||||
}
|
||||
case '(':
|
||||
// complex_lit
|
||||
p.next()
|
||||
re := p.parseNumber()
|
||||
p.expect('+')
|
||||
im := p.parseNumber()
|
||||
p.expect(')')
|
||||
x = Const{cmplx{re.val.(*big.Rat), im.val.(*big.Rat)}}
|
||||
typ = Complex128.Underlying
|
||||
case scanner.String:
|
||||
// string_lit
|
||||
x = MakeConst(token.STRING, p.lit)
|
||||
p.next()
|
||||
typ = String.Underlying
|
||||
default:
|
||||
p.error("expected literal")
|
||||
}
|
||||
if obj.Type == nil {
|
||||
obj.Type = typ
|
||||
}
|
||||
_ = x // TODO(gri) store x somewhere
|
||||
}
|
||||
|
||||
|
||||
// TypeDecl = "type" ExportedName Type .
|
||||
//
|
||||
func (p *gcParser) parseTypeDecl() {
|
||||
p.expectKeyword("type")
|
||||
obj := p.parseExportedName(ast.Typ)
|
||||
typ := p.parseType()
|
||||
|
||||
name := obj.Type.(*Name)
|
||||
assert(name.Underlying == nil)
|
||||
assert(Underlying(typ) == typ)
|
||||
name.Underlying = typ
|
||||
}
|
||||
|
||||
|
||||
// VarDecl = "var" ExportedName Type .
|
||||
//
|
||||
func (p *gcParser) parseVarDecl() {
|
||||
p.expectKeyword("var")
|
||||
obj := p.parseExportedName(ast.Var)
|
||||
obj.Type = p.parseType()
|
||||
}
|
||||
|
||||
|
||||
// FuncDecl = "func" ExportedName Signature .
|
||||
//
|
||||
func (p *gcParser) parseFuncDecl() {
|
||||
// "func" already consumed
|
||||
obj := p.parseExportedName(ast.Fun)
|
||||
obj.Type = p.parseFuncType()
|
||||
}
|
||||
|
||||
|
||||
// MethodDecl = "func" Receiver identifier Signature .
|
||||
// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
|
||||
//
|
||||
func (p *gcParser) parseMethodDecl() {
|
||||
// "func" already consumed
|
||||
scope := ast.NewScope(nil) // method scope
|
||||
p.expect('(')
|
||||
p.parseParameter(scope, nil) // receiver
|
||||
p.expect(')')
|
||||
p.expect(scanner.Ident)
|
||||
isVariadic := false
|
||||
p.parseSignature(scope, &isVariadic)
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
|
||||
//
|
||||
func (p *gcParser) parseDecl() {
|
||||
switch p.lit {
|
||||
case "import":
|
||||
p.parseImportDecl()
|
||||
case "const":
|
||||
p.parseConstDecl()
|
||||
case "type":
|
||||
p.parseTypeDecl()
|
||||
case "var":
|
||||
p.parseVarDecl()
|
||||
case "func":
|
||||
p.next() // look ahead
|
||||
if p.tok == '(' {
|
||||
p.parseMethodDecl()
|
||||
} else {
|
||||
p.parseFuncDecl()
|
||||
}
|
||||
}
|
||||
p.expect('\n')
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Export
|
||||
|
||||
// Export = "PackageClause { Decl } "$$" .
|
||||
// PackageClause = "package" identifier [ "safe" ] "\n" .
|
||||
//
|
||||
func (p *gcParser) parseExport() (string, *ast.Scope) {
|
||||
p.expectKeyword("package")
|
||||
name := p.expect(scanner.Ident)
|
||||
if p.tok != '\n' {
|
||||
// A package is safe if it was compiled with the -u flag,
|
||||
// which disables the unsafe package.
|
||||
// TODO(gri) remember "safe" package
|
||||
p.expectKeyword("safe")
|
||||
}
|
||||
p.expect('\n')
|
||||
|
||||
for p.tok != '$' && p.tok != scanner.EOF {
|
||||
p.parseDecl()
|
||||
}
|
||||
|
||||
if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
|
||||
// don't call next()/expect() since reading past the
|
||||
// export data may cause scanner errors (e.g. NUL chars)
|
||||
p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
|
||||
}
|
||||
|
||||
if n := p.scanner.ErrorCount; n != 0 {
|
||||
p.errorf("expected no scanner errors, got %d", n)
|
||||
}
|
||||
|
||||
return name, p.scope
|
||||
}
|
106
src/pkg/go/types/gcimporter_test.go
Normal file
106
src/pkg/go/types/gcimporter_test.go
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"exec"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
var gcName, gcPath string // compiler name and path
|
||||
|
||||
func init() {
|
||||
// find a compiler
|
||||
for _, char := range []string{"5", "6", "8"} {
|
||||
var err os.Error
|
||||
gcName = char + "g"
|
||||
gcPath, err = exec.LookPath(gcName)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func compile(t *testing.T, dirname, filename string) {
|
||||
cmd, err := exec.Run(gcPath, []string{gcPath, filename}, nil, dirname, exec.DevNull, exec.Pipe, exec.MergeWithStdout)
|
||||
if err != nil {
|
||||
t.Errorf("%s %s failed: %s", gcName, filename, err)
|
||||
return
|
||||
}
|
||||
defer cmd.Close()
|
||||
|
||||
msg, err := cmd.Wait(0)
|
||||
if err != nil {
|
||||
t.Errorf("%s %s failed: %s", gcName, filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !msg.Exited() || msg.ExitStatus() != 0 {
|
||||
t.Errorf("%s %s failed: exit status = %d", gcName, filename, msg.ExitStatus())
|
||||
output, _ := ioutil.ReadAll(cmd.Stdout)
|
||||
t.Log(string(output))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func testPath(t *testing.T, path string) bool {
|
||||
_, _, err := GcImporter(path)
|
||||
if err != nil {
|
||||
t.Errorf("testPath(%s): %s", path, err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
const maxTime = 3e9 // maximum allotted testing time in ns
|
||||
|
||||
func testDir(t *testing.T, dir string, endTime int64) (nimports int) {
|
||||
dirname := filepath.Join(pkgRoot, dir)
|
||||
list, err := ioutil.ReadDir(dirname)
|
||||
if err != nil {
|
||||
t.Errorf("testDir(%s): %s", dirname, err)
|
||||
}
|
||||
for _, f := range list {
|
||||
if time.Nanoseconds() >= endTime {
|
||||
t.Log("testing time used up")
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case f.IsRegular():
|
||||
// try extensions
|
||||
for _, ext := range pkgExts {
|
||||
if strings.HasSuffix(f.Name, ext) {
|
||||
name := f.Name[0 : len(f.Name)-len(ext)] // remove extension
|
||||
if testPath(t, filepath.Join(dir, name)) {
|
||||
nimports++
|
||||
}
|
||||
}
|
||||
}
|
||||
case f.IsDirectory():
|
||||
nimports += testDir(t, filepath.Join(dir, f.Name), endTime)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func TestGcImport(t *testing.T) {
|
||||
compile(t, "testdata", "exports.go")
|
||||
|
||||
nimports := 0
|
||||
if testPath(t, "./testdata/exports") {
|
||||
nimports++
|
||||
}
|
||||
nimports += testDir(t, "", time.Nanoseconds()+maxTime) // installed packages
|
||||
t.Logf("tested %d imports", nimports)
|
||||
}
|
89
src/pkg/go/types/testdata/exports.go
vendored
Normal file
89
src/pkg/go/types/testdata/exports.go
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file is used to generate a .6 object file which
|
||||
// serves as test file for gcimporter_test.go.
|
||||
|
||||
package exports
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
|
||||
const (
|
||||
C0 int = 0
|
||||
C1 = 3.14159265
|
||||
C2 = 2.718281828i
|
||||
C3 = -123.456e-789
|
||||
C4 = +123.456E+789
|
||||
C5 = 1234i
|
||||
C6 = "foo\n"
|
||||
C7 = `bar\n`
|
||||
)
|
||||
|
||||
|
||||
type (
|
||||
T1 int
|
||||
T2 [10]int
|
||||
T3 []int
|
||||
T4 *int
|
||||
T5 chan int
|
||||
T6a chan<- int
|
||||
T6b chan (<-chan int)
|
||||
T6c chan<- (chan int)
|
||||
T7 <-chan *ast.File
|
||||
T8 struct{}
|
||||
T9 struct {
|
||||
a int
|
||||
b, c float32
|
||||
d []string "tag"
|
||||
}
|
||||
T10 struct {
|
||||
T8
|
||||
T9
|
||||
_ *T10
|
||||
}
|
||||
T11 map[int]string
|
||||
T12 interface{}
|
||||
T13 interface {
|
||||
m1()
|
||||
m2(int) float32
|
||||
}
|
||||
T14 interface {
|
||||
T12
|
||||
T13
|
||||
m3(x ...struct{}) []T9
|
||||
}
|
||||
T15 func()
|
||||
T16 func(int)
|
||||
T17 func(x int)
|
||||
T18 func() float32
|
||||
T19 func() (x float32)
|
||||
T20 func(...interface{})
|
||||
T21 struct{ next *T21 }
|
||||
T22 struct{ link *T23 }
|
||||
T23 struct{ link *T22 }
|
||||
T24 *T24
|
||||
T25 *T26
|
||||
T26 *T27
|
||||
T27 *T25
|
||||
T28 func(T28) T28
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
V0 int
|
||||
V1 = -991.0
|
||||
)
|
||||
|
||||
|
||||
func F1() {}
|
||||
func F2(x int) {}
|
||||
func F3() int { return 0 }
|
||||
func F4() float32 { return 0 }
|
||||
func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
|
||||
|
||||
|
||||
func (p *T1) M1()
|
122
src/pkg/go/types/types.go
Normal file
122
src/pkg/go/types/types.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// PACKAGE UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE.
|
||||
// The types package declares the types used to represent Go types.
|
||||
//
|
||||
package types
|
||||
|
||||
import "go/ast"
|
||||
|
||||
|
||||
// All types implement the Type interface.
|
||||
type Type interface {
|
||||
isType()
|
||||
}
|
||||
|
||||
|
||||
// All concrete types embed ImplementsType which
|
||||
// ensures that all types implement the Type interface.
|
||||
type ImplementsType struct{}
|
||||
|
||||
func (t *ImplementsType) isType() {}
|
||||
|
||||
|
||||
// A Basic represents a (unnamed) basic type.
|
||||
type Basic struct {
|
||||
ImplementsType
|
||||
// TODO(gri) need a field specifying the exact basic type
|
||||
}
|
||||
|
||||
|
||||
// An Array represents an array type [Len]Elt.
|
||||
type Array struct {
|
||||
ImplementsType
|
||||
Len uint64
|
||||
Elt Type
|
||||
}
|
||||
|
||||
|
||||
// A Slice represents a slice type []Elt.
|
||||
type Slice struct {
|
||||
ImplementsType
|
||||
Elt Type
|
||||
}
|
||||
|
||||
|
||||
// A Struct represents a struct type struct{...}.
|
||||
type Struct struct {
|
||||
ImplementsType
|
||||
// TODO(gri) need to remember fields.
|
||||
}
|
||||
|
||||
|
||||
// A Pointer represents a pointer type *Base.
|
||||
type Pointer struct {
|
||||
ImplementsType
|
||||
Base Type
|
||||
}
|
||||
|
||||
|
||||
// A Func represents a function type func(...) (...).
|
||||
type Func struct {
|
||||
ImplementsType
|
||||
IsVariadic bool
|
||||
// TODO(gri) need to remember parameters.
|
||||
}
|
||||
|
||||
|
||||
// An Interface represents an interface type interface{...}.
|
||||
type Interface struct {
|
||||
ImplementsType
|
||||
// TODO(gri) need to remember methods.
|
||||
}
|
||||
|
||||
|
||||
// A Map represents a map type map[Key]Elt.
|
||||
type Map struct {
|
||||
ImplementsType
|
||||
Key, Elt Type
|
||||
}
|
||||
|
||||
|
||||
// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
|
||||
type Chan struct {
|
||||
ImplementsType
|
||||
Dir ast.ChanDir
|
||||
Elt Type
|
||||
}
|
||||
|
||||
|
||||
// A Name represents a named type as declared in a type declaration.
|
||||
type Name struct {
|
||||
ImplementsType
|
||||
Underlying Type // nil if not fully declared
|
||||
Obj *ast.Object // corresponding declared object
|
||||
// TODO(gri) need to remember fields and methods.
|
||||
}
|
||||
|
||||
|
||||
// If typ is a pointer type, Deref returns the pointer's base type;
|
||||
// otherwise it returns typ.
|
||||
func Deref(typ Type) Type {
|
||||
if typ, ok := typ.(*Pointer); ok {
|
||||
return typ.Base
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
|
||||
// Underlying returns the underlying type of a type.
|
||||
func Underlying(typ Type) Type {
|
||||
if typ, ok := typ.(*Name); ok {
|
||||
utyp := typ.Underlying
|
||||
if _, ok := utyp.(*Basic); ok {
|
||||
return typ
|
||||
}
|
||||
return utyp
|
||||
|
||||
}
|
||||
return typ
|
||||
}
|
113
src/pkg/go/types/universe.go
Normal file
113
src/pkg/go/types/universe.go
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// FILE UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE.
|
||||
// This file implements the universe and unsafe package scopes.
|
||||
|
||||
package types
|
||||
|
||||
import "go/ast"
|
||||
|
||||
|
||||
var (
|
||||
scope, // current scope to use for initialization
|
||||
Universe,
|
||||
Unsafe *ast.Scope
|
||||
)
|
||||
|
||||
|
||||
func define(kind ast.ObjKind, name string) *ast.Object {
|
||||
obj := ast.NewObj(kind, name)
|
||||
if scope.Insert(obj) != nil {
|
||||
panic("types internal error: double declaration")
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
|
||||
func defType(name string) *Name {
|
||||
obj := define(ast.Typ, name)
|
||||
typ := &Name{Underlying: &Basic{}, Obj: obj}
|
||||
obj.Type = typ
|
||||
return typ
|
||||
}
|
||||
|
||||
|
||||
func defConst(name string) {
|
||||
obj := define(ast.Con, name)
|
||||
_ = obj // TODO(gri) fill in other properties
|
||||
}
|
||||
|
||||
|
||||
func defFun(name string) {
|
||||
obj := define(ast.Fun, name)
|
||||
_ = obj // TODO(gri) fill in other properties
|
||||
}
|
||||
|
||||
|
||||
var (
|
||||
Bool,
|
||||
Int,
|
||||
Float64,
|
||||
Complex128,
|
||||
String *Name
|
||||
)
|
||||
|
||||
|
||||
func init() {
|
||||
Universe = ast.NewScope(nil)
|
||||
scope = Universe
|
||||
|
||||
Bool = defType("bool")
|
||||
defType("byte") // TODO(gri) should be an alias for uint8
|
||||
defType("complex64")
|
||||
Complex128 = defType("complex128")
|
||||
defType("float32")
|
||||
Float64 = defType("float64")
|
||||
defType("int8")
|
||||
defType("int16")
|
||||
defType("int32")
|
||||
defType("int64")
|
||||
String = defType("string")
|
||||
defType("uint8")
|
||||
defType("uint16")
|
||||
defType("uint32")
|
||||
defType("uint64")
|
||||
Int = defType("int")
|
||||
defType("uint")
|
||||
defType("uintptr")
|
||||
|
||||
defConst("true")
|
||||
defConst("false")
|
||||
defConst("iota")
|
||||
defConst("nil")
|
||||
|
||||
defFun("append")
|
||||
defFun("cap")
|
||||
defFun("close")
|
||||
defFun("complex")
|
||||
defFun("copy")
|
||||
defFun("imag")
|
||||
defFun("len")
|
||||
defFun("make")
|
||||
defFun("new")
|
||||
defFun("panic")
|
||||
defFun("print")
|
||||
defFun("println")
|
||||
defFun("real")
|
||||
defFun("recover")
|
||||
|
||||
Unsafe = ast.NewScope(nil)
|
||||
scope = Unsafe
|
||||
defType("Pointer")
|
||||
|
||||
defFun("Alignof")
|
||||
defFun("New")
|
||||
defFun("NewArray")
|
||||
defFun("Offsetof")
|
||||
defFun("Reflect")
|
||||
defFun("Sizeof")
|
||||
defFun("Typeof")
|
||||
defFun("Unreflect")
|
||||
}
|
Loading…
Reference in New Issue
Block a user