Fixes #15323.
Change-Id: I50e996e6fde6b24327cb45dd84da31deef4dcc56
Reviewed-on: https://go-review.googlesource.com/27171
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
// errors.
// If disabled, only named types are tracked, possibly leading to slightly
// less efficient encoding in rare cases. It also prevents the export of
-// some corner-case type declarations (but those are not handled correctly
-// with with the textual export format either).
+// some corner-case type declarations (but those were not handled correctly
+// with the former textual export format either).
// TODO(gri) enable and remove once issues caused by it are fixed
const trackAllTypes = false
p.pkg()
// defer some type-checking until all types are read in completely
- // (parser.go:import_package)
tcok := typecheckok
typecheckok = true
defercheckwidth()
"bytes"
"cmd/internal/bio"
"fmt"
- "sort"
"unicode"
"unicode/utf8"
)
var (
- newexport bool // if set, use new export format
- Debug_export int // if set, print debugging information about export data
+ Debug_export int // if set, print debugging information about export data
exportsize int
)
}
}
-func dumppkg(p *Pkg) {
- if p == nil || p == localpkg || p.Exported || p == builtinpkg {
- return
- }
- p.Exported = true
- suffix := ""
- if !p.Direct {
- suffix = " // indirect"
- }
- exportf("\timport %s %q%s\n", p.Name, p.Path, suffix)
-}
-
// Look for anything we need for the inline body
func reexportdeplist(ll Nodes) {
for _, n := range ll.Slice() {
reexportdeplist(n.Nbody)
}
-func dumpexportconst(s *Sym) {
- n := typecheck(s.Def, Erv)
- if n == nil || n.Op != OLITERAL {
- Fatalf("dumpexportconst: oconst nil: %v", s)
- }
-
- t := n.Type // may or may not be specified
- dumpexporttype(t)
-
- if t != nil && !t.IsUntyped() {
- exportf("\tconst %v %v = %v\n", sconv(s, FmtSharp), Tconv(t, FmtSharp), vconv(n.Val(), FmtSharp))
- } else {
- exportf("\tconst %v = %v\n", sconv(s, FmtSharp), vconv(n.Val(), FmtSharp))
- }
-}
-
-func dumpexportvar(s *Sym) {
- n := s.Def
- n = typecheck(n, Erv|Ecall)
- if n == nil || n.Type == nil {
- Yyerror("variable exported but not defined: %v", s)
- return
- }
-
- t := n.Type
- dumpexporttype(t)
-
- if t.Etype == TFUNC && n.Class == PFUNC {
- if n.Func != nil && n.Func.Inl.Len() != 0 {
- // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
- // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
- if Debug['l'] < 2 {
- typecheckinl(n)
- }
-
- // NOTE: The space after %#S here is necessary for ld's export data parser.
- exportf("\tfunc %v %v { %v }\n", sconv(s, FmtSharp), Tconv(t, FmtShort|FmtSharp), hconv(n.Func.Inl, FmtSharp|FmtBody))
-
- reexportdeplist(n.Func.Inl)
- } else {
- exportf("\tfunc %v %v\n", sconv(s, FmtSharp), Tconv(t, FmtShort|FmtSharp))
- }
- } else {
- exportf("\tvar %v %v\n", sconv(s, FmtSharp), Tconv(t, FmtSharp))
- }
-}
-
// methodbyname sorts types by symbol name.
type methodbyname []*Field
func (x methodbyname) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x methodbyname) Less(i, j int) bool { return x[i].Sym.Name < x[j].Sym.Name }
-func dumpexporttype(t *Type) {
- if t == nil {
- return
- }
- if t.Printed || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype {
- return
- }
- t.Printed = true
-
- if t.Sym != nil {
- dumppkg(t.Sym.Pkg)
- }
-
- switch t.Etype {
- case TSTRUCT, TINTER:
- for _, f := range t.Fields().Slice() {
- dumpexporttype(f.Type)
- }
- case TFUNC:
- dumpexporttype(t.Recvs())
- dumpexporttype(t.Results())
- dumpexporttype(t.Params())
- case TMAP:
- dumpexporttype(t.Val())
- dumpexporttype(t.Key())
- case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
- dumpexporttype(t.Elem())
- }
-
- if t.Sym == nil {
- return
- }
-
- var m []*Field
- for _, f := range t.Methods().Slice() {
- dumpexporttype(f.Type)
- m = append(m, f)
- }
- sort.Sort(methodbyname(m))
-
- exportf("\ttype %v %v\n", sconv(t.Sym, FmtSharp), Tconv(t, FmtSharp|FmtLong))
- for _, f := range m {
- if f.Nointerface {
- exportf("\t//go:nointerface\n")
- }
- if f.Type.Nname() != nil && f.Type.Nname().Func.Inl.Len() != 0 { // nname was set by caninl
-
- // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
- // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
- if Debug['l'] < 2 {
- typecheckinl(f.Type.Nname())
- }
- exportf("\tfunc %v %v %v { %v }\n", Tconv(f.Type.Recvs(), FmtSharp), sconv(f.Sym, FmtShort|FmtByte|FmtSharp), Tconv(f.Type, FmtShort|FmtSharp), hconv(f.Type.Nname().Func.Inl, FmtSharp|FmtBody))
- reexportdeplist(f.Type.Nname().Func.Inl)
- } else {
- exportf("\tfunc %v %v %v\n", Tconv(f.Type.Recvs(), FmtSharp), sconv(f.Sym, FmtShort|FmtByte|FmtSharp), Tconv(f.Type, FmtShort|FmtSharp))
- }
- }
-}
-
-func dumpsym(s *Sym) {
- if s.Flags&SymExported != 0 {
- return
- }
- s.Flags |= SymExported
-
- if s.Def == nil {
- Yyerror("unknown export symbol: %v", s)
- return
- }
-
- // print("dumpsym %O %+S\n", s->def->op, s);
- dumppkg(s.Pkg)
-
- switch s.Def.Op {
- default:
- Yyerror("unexpected export symbol: %v %v", s.Def.Op, s)
-
- case OLITERAL:
- dumpexportconst(s)
-
- case OTYPE:
- if s.Def.Type.Etype == TFORW {
- Yyerror("export of incomplete type %v", s)
- } else {
- dumpexporttype(s.Def.Type)
- }
-
- case ONAME:
- dumpexportvar(s)
- }
-}
-
func dumpexport() {
if buildid != "" {
exportf("build id %q\n", buildid)
}
size := 0 // size of export section without enclosing markers
- if newexport {
- // binary export
- // The linker also looks for the $$ marker - use char after $$ to distinguish format.
- exportf("\n$$B\n") // indicate binary format
- if debugFormat {
- // save a copy of the export data
- var copy bytes.Buffer
- bcopy := bufio.NewWriter(©)
- size = export(bcopy, Debug_export != 0)
- bcopy.Flush() // flushing to bytes.Buffer cannot fail
- if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
- Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
- }
- // export data must contain no '$' so that we can find the end by searching for "$$"
- if bytes.IndexByte(copy.Bytes(), '$') >= 0 {
- Fatalf("export data contains $")
- }
-
- // verify that we can read the copied export data back in
- // (use empty package map to avoid collisions)
- savedPkgMap := pkgMap
- savedPkgs := pkgs
- pkgMap = make(map[string]*Pkg)
- pkgs = nil
- importpkg = mkpkg("")
- Import(bufio.NewReader(©)) // must not die
- importpkg = nil
- pkgs = savedPkgs
- pkgMap = savedPkgMap
- } else {
- size = export(bout.Writer, Debug_export != 0)
+ // The linker also looks for the $$ marker - use char after $$ to distinguish format.
+ exportf("\n$$B\n") // indicate binary export format
+ if debugFormat {
+ // save a copy of the export data
+ var copy bytes.Buffer
+ bcopy := bufio.NewWriter(©)
+ size = export(bcopy, Debug_export != 0)
+ bcopy.Flush() // flushing to bytes.Buffer cannot fail
+ if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
+ Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
}
- exportf("\n$$\n")
- } else {
- // textual export
- lno := lineno
-
- exportf("\n$$\n") // indicate textual format
- exportsize = 0
- exportf("package %s", localpkg.Name)
- if safemode {
- exportf(" safe")
+ // export data must contain no '$' so that we can find the end by searching for "$$"
+ // TODO(gri) is this still needed?
+ if bytes.IndexByte(copy.Bytes(), '$') >= 0 {
+ Fatalf("export data contains $")
}
- exportf("\n")
- for _, p := range pkgs {
- if p.Direct {
- dumppkg(p)
- }
- }
-
- // exportlist grows during iteration - cannot use range
- for i := 0; i < len(exportlist); i++ {
- n := exportlist[i]
- lineno = n.Lineno
- dumpsym(n.Sym)
- }
-
- size = exportsize
- exportf("\n$$\n")
- lineno = lno
+ // verify that we can read the copied export data back in
+ // (use empty package map to avoid collisions)
+ savedPkgMap := pkgMap
+ savedPkgs := pkgs
+ pkgMap = make(map[string]*Pkg)
+ pkgs = nil
+ importpkg = mkpkg("")
+ Import(bufio.NewReader(©)) // must not die
+ importpkg = nil
+ pkgs = savedPkgs
+ pkgMap = savedPkgMap
+ } else {
+ size = export(bout.Writer, Debug_export != 0)
}
+ exportf("\n$$\n")
if Debug_export != 0 {
fmt.Printf("export data size = %d bytes\n", size)
const (
FErr = iota
FDbg
- FExp
+ _ // formerly FExp - leave gap for now just in case there's some hard-wired dependency on the const value
FTypeId
)
if *flags&FmtSign != 0 {
fmtmode = FDbg
} else if *flags&FmtSharp != 0 {
- fmtmode = FExp
+ // for textual export format - no longer supported
+ Fatalf("textual export format request")
} else if *flags&FmtLeft != 0 {
fmtmode = FTypeId
}
switch u := v.U.(type) {
case *Mpint:
if !u.Rune {
- if (flag&FmtSharp != 0) || fmtmode == FExp {
+ if flag&FmtSharp != 0 {
return bconv(u, FmtSharp)
}
return bconv(u, 0)
return fmt.Sprintf("('\\x00' + %v)", u)
case *Mpflt:
- if (flag&FmtSharp != 0) || fmtmode == FExp {
+ if flag&FmtSharp != 0 {
return fconv(u, 0)
}
return fconv(u, FmtSharp)
case *Mpcplx:
- if (flag&FmtSharp != 0) || fmtmode == FExp {
+ if flag&FmtSharp != 0 {
return fmt.Sprintf("(%v+%vi)", &u.Real, &u.Imag)
}
if v.U.(*Mpcplx).Real.CmpFloat64(0) == 0 {
return s.Pkg.Name + "." + s.Name // dcommontype, typehash
}
return s.Pkg.Prefix + "." + s.Name // (methodsym), typesym, weaksym
-
- case FExp:
- if s.Name != "" && s.Name[0] == '.' {
- Fatalf("exporting synthetic symbol %s", s.Name)
- }
- if s.Pkg != builtinpkg {
- return fmt.Sprintf("@%q.%s", s.Pkg.Path, s.Name)
- }
}
}
p = s.Name[i+1:]
}
- // exportname needs to see the name without the prefix too.
- if (fmtmode == FExp && !exportname(p)) || fmtmode == FDbg {
+ if fmtmode == FDbg {
return fmt.Sprintf("@%q.%s", s.Pkg.Path, p)
}
if flag&FmtUnsigned != 0 {
return sconv(t.Sym, FmtUnsigned)
}
- fallthrough
- case FExp:
if t.Sym.Pkg == localpkg && t.Vargen != 0 {
return fmt.Sprintf("%v·%d", t.Sym, t.Vargen)
}
switch t.Results().NumFields() {
case 0:
- break
+ // nothing to do
case 1:
- if fmtmode != FExp {
- buf.WriteString(" ")
- buf.WriteString(Tconv(t.Results().Field(0).Type, 0)) // struct->field->field's type
- break
- }
- fallthrough
+ buf.WriteString(" ")
+ buf.WriteString(Tconv(t.Results().Field(0).Type, 0)) // struct->field->field's type
default:
buf.WriteString(" ")
return "undefined"
case TUNSAFEPTR:
- if fmtmode == FExp {
- return "@\"unsafe\".Pointer"
- }
return "unsafe.Pointer"
case TDDDFIELD:
- if fmtmode == FExp {
- Fatalf("cannot use TDDDFIELD with old exporter")
- }
return fmt.Sprintf("%v <%v> %v", t.Etype, t.Sym, t.DDDField())
case Txxx:
return "Txxx"
}
- if fmtmode == FExp {
- Fatalf("missing %v case during export", t.Etype)
- }
-
// Don't know how to handle - fall back to detailed prints.
return fmt.Sprintf("%v <%v> %v", t.Etype, t.Sym, t.Elem())
}
switch n.Op {
case ODCL:
- if fmtmode == FExp {
- switch n.Left.Class {
- case PPARAM, PPARAMOUT, PAUTO, PAUTOHEAP:
- f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type)
- goto ret
- }
- }
-
f += fmt.Sprintf("var %v %v", n.Left.Sym, n.Left.Type)
case ODCLFIELD:
// preceded by the DCL which will be re-parsed and typechecked to reproduce
// the "v = <N>" again.
case OAS, OASWB:
- if fmtmode == FExp && n.Right == nil {
- break
- }
-
if n.Colas && !complexinit {
f += fmt.Sprintf("%v := %v", n.Left, n.Right)
} else {
f += fmt.Sprintf("%v: ", n.Left)
}
-ret:
if extrablock {
f += "}"
}
// Special case: name used as local variable in export.
// _ becomes ~b%d internally; print as _ for export
case ONAME:
- if (fmtmode == FExp || fmtmode == FErr) && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
+ if fmtmode == FErr && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
return "_"
}
- if fmtmode == FExp && n.Sym != nil && !isblank(n) && n.Name.Vargen > 0 {
- return fmt.Sprintf("%v·%d", n.Sym, n.Name.Vargen)
- }
-
- // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
- // but for export, this should be rendered as (*pkg.T).meth.
- // These nodes have the special property that they are names with a left OTYPE and a right ONAME.
- if fmtmode == FExp && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME {
- if n.Left.Type.IsPtr() {
- return fmt.Sprintf("(%v).%v", n.Left.Type, sconv(n.Right.Sym, FmtShort|FmtByte))
- } else {
- return fmt.Sprintf("%v.%v", n.Left.Type, sconv(n.Right.Sym, FmtShort|FmtByte))
- }
- }
fallthrough
case OPACK, ONONAME:
return "composite literal"
}
- if fmtmode == FExp && ptrlit {
- // typecheck has overwritten OIND by OTYPE with pointer type.
- return fmt.Sprintf("(&%v{ %v })", n.Right.Type.Elem(), hconv(n.List, FmtComma))
- }
-
return fmt.Sprintf("(%v{ %v })", n.Right, hconv(n.List, FmtComma))
case OPTRLIT:
- if fmtmode == FExp && n.Left.Implicit {
- return Nconv(n.Left, 0)
- }
return fmt.Sprintf("&%v", n.Left)
- case OSTRUCTLIT:
- if fmtmode == FExp { // requires special handling of field names
- var f string
- if n.Implicit {
- f += "{"
- } else {
- f += fmt.Sprintf("(%v{", n.Type)
- }
- for i1, n1 := range n.List.Slice() {
- f += fmt.Sprintf(" %v:%v", sconv(n1.Left.Sym, FmtShort|FmtByte), n1.Right)
-
- if i1+1 < n.List.Len() {
- f += ","
- } else {
- f += " "
- }
- }
-
- if !n.Implicit {
- f += "})"
- return f
- }
- f += "}"
- return f
- }
- fallthrough
-
- case OARRAYLIT, OMAPLIT:
+ case OSTRUCTLIT, OARRAYLIT, OMAPLIT:
if fmtmode == FErr {
return fmt.Sprintf("%v literal", n.Type)
}
- if fmtmode == FExp && n.Implicit {
- return fmt.Sprintf("{ %v }", hconv(n.List, FmtComma))
- }
return fmt.Sprintf("(%v{ %v })", n.Type, hconv(n.List, FmtComma))
case OKEY:
if n.Left != nil && n.Right != nil {
- if fmtmode == FExp && n.Left.Type == structkey {
- // requires special handling of field names
- return fmt.Sprintf("%v:%v", sconv(n.Left.Sym, FmtShort|FmtByte), n.Right)
- } else {
- return fmt.Sprintf("%v:%v", n.Left, n.Right)
- }
+ return fmt.Sprintf("%v:%v", n.Left, n.Right)
}
if n.Left == nil && n.Right != nil {
// we almost always want the original, except in export mode for literals
// this saves the importer some work, and avoids us having to redo some
// special casing for package unsafe
- if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil {
+ if n.Op != OLITERAL && n.Orig != nil {
n = n.Orig
}
// Take the name from the original, lest we substituted it with ~r%d or ~b%d.
// ~r%d is a (formerly) unnamed result.
- if (fmtmode == FErr || fmtmode == FExp) && f.Nname != nil {
+ if fmtmode == FErr && f.Nname != nil {
if f.Nname.Orig != nil {
s = f.Nname.Orig.Sym
if s != nil && s.Name[0] == '~' {
} else {
name = sconv(s, 0)
}
- } else if fmtmode == FExp {
- if f.Embedded != 0 && s.Pkg != nil && len(s.Pkg.Path) > 0 {
- name = fmt.Sprintf("@%q.?", s.Pkg.Path)
- } else {
- name = "?"
- }
}
}
var str string
switch fmtmode {
- case FErr, FExp:
+ case FErr:
str = nodefmt(n, flag)
case FDbg:
l.nlsemi = true
goto lx
- case '#', '$', '?', '@', '\\':
- if importpkg != nil {
- goto lx
- }
- fallthrough
-
default:
// anything else is illegal
Yyerror("syntax error: illegal character %#U", c)
// general case
for {
if c >= utf8.RuneSelf {
- if unicode.IsLetter(c) || c == '_' || unicode.IsDigit(c) || importpkg != nil && c == 0xb7 {
+ if unicode.IsLetter(c) || c == '_' || unicode.IsDigit(c) {
if cp.Len() == 0 && unicode.IsDigit(c) {
Yyerror("identifier cannot begin with digit %#U", c)
}
cp.WriteByte(byte(c))
c = l.getr()
}
- // Falling through to exponent parsing here permits invalid
- // floating-point numbers with fractional mantissa and base-2
- // (p or P) exponent. We don't care because base-2 exponents
- // can only show up in machine-generated textual export data
- // which will use correct formatting.
}
// exponent
- // base-2 exponent (p or P) is only allowed in export data (see #9036)
- // TODO(gri) Once we switch to binary import data, importpkg will
- // always be nil in this function. Simplify the code accordingly.
- if c == 'e' || c == 'E' || importpkg != nil && (c == 'p' || c == 'P') {
+ if c == 'e' || c == 'E' {
isInt = false
cp.WriteByte(byte(c))
c = l.getr()
case 0:
yyerrorl(lexlineno, "illegal NUL byte")
case '\n':
- if importpkg == nil {
- lexlineno++
- }
+ lexlineno++
case utf8.RuneError:
if w == 1 {
yyerrorl(lexlineno, "illegal UTF-8 sequence")
obj.Flagcount("live", "debug liveness analysis", &debuglive)
obj.Flagcount("m", "print optimization decisions", &Debug['m'])
flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
- flag.BoolVar(&newexport, "newexport", true, "use new export format") // TODO(gri) remove eventually (issue 15323)
flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports")
flag.StringVar(&outfile, "o", "", "write output to `file`")
flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
iota_ = -1000000
incannedimport = 1
- // The first byte in the binary export format is a 'c' or 'd'
- // specifying the encoding format. We could just check that
- // byte, but this is a perhaps more robust. Also, it is not
- // speed-critical.
- // TODO(gri) simplify once textual export format has gone
- if strings.HasPrefix(runtimeimport, "package") {
- // textual export format
- importpkg = Runtimepkg
- parse_import(bufio.NewReader(strings.NewReader(runtimeimport)), nil)
- importpkg = unsafepkg
- parse_import(bufio.NewReader(strings.NewReader(unsafeimport)), nil)
- } else {
- // binary export format
- importpkg = Runtimepkg
- Import(bufio.NewReader(strings.NewReader(runtimeimport)))
- importpkg = unsafepkg
- Import(bufio.NewReader(strings.NewReader(unsafeimport)))
- }
+ importpkg = Runtimepkg
+ Import(bufio.NewReader(strings.NewReader(runtimeimport)))
+ importpkg = unsafepkg
+ Import(bufio.NewReader(strings.NewReader(unsafeimport)))
importpkg = nil
incannedimport = 0
linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib
// In the importfile, if we find:
- // $$\n (old format): position the input right after $$\n and return
- // $$B\n (new format): import directly, then feed the lexer a dummy statement
+ // $$\n (textual format): not supported anymore
+ // $$B\n (binary format) : import directly, then feed the lexer a dummy statement
// look for $$
var c byte
switch c {
case '\n':
- // old export format
- parse_import(imp, indent)
+ Yyerror("cannot import %s: old export format no longer supported (recompile library)", path_)
case 'B':
- // new export format
if Debug_export != 0 {
fmt.Printf("importing %s (%s)\n", path_, file)
}
}
// Look for $$B that introduces binary export data.
- textual := false // TODO(gri) remove once we switched to binary export format
i := bytes.Index(b, []byte("\n$$B\n"))
if i < 0 {
- // Look for $$ that introduces textual export data.
- i = bytes.Index(b, []byte("\n$$\n"))
- if i < 0 {
- log.Fatal("did not find beginning of export data")
- }
- textual = true
- i-- // textual data doesn't have B
+ log.Fatal("did not find beginning of export data")
}
b = b[i+5:]
b = b[:i+4]
// Process and reformat export data.
+ const n = 40 // number of bytes per line
fmt.Fprintf(w, "\nconst %simport = \"\"", name)
- if textual {
- for _, p := range bytes.SplitAfter(b, []byte("\n")) {
- // Chop leading white space.
- p = bytes.TrimLeft(p, " \t")
- if len(p) == 0 {
- continue
- }
-
- fmt.Fprintf(w, " +\n\t%q", p)
- }
- } else {
- const n = 40 // number of bytes per line
- for len(b) > 0 {
- i := len(b)
- if i > n {
- i = n
- }
- fmt.Fprintf(w, " +\n\t%q", b[:i])
- b = b[i:]
+ for len(b) > 0 {
+ i := len(b)
+ if i > n {
+ i = n
}
+ fmt.Fprintf(w, " +\n\t%q", b[:i])
+ b = b[i:]
}
fmt.Fprintf(w, "\n")
}
const trace = false // if set, parse tracing can be enabled with -x
-// parse_import parses the export data of a package that is imported.
-func parse_import(bin *bufio.Reader, indent []byte) {
- newparser(bin, indent).import_package()
-}
-
// parse_file parses a single Go source file.
func parse_file(bin *bufio.Reader) {
newparser(bin, nil).file()
fnest int // function nesting level (for error handling)
xnest int // expression nesting level (for complit ambiguity resolution)
indent []byte // tracing support
-
- // TODO(gri) remove this once we switch to binary export format
- structpkg *Pkg // for verification in addmethod only
}
// newparser returns a new parser ready to parse from src.
var my *Sym
switch p.tok {
- case LNAME, '@', '?':
+ case LNAME:
// import with given name
my = p.sym()
my.Block = 1 // at top level
}
-// import_package parses the header of an imported package as exported
-// in textual format from another package.
-func (p *parser) import_package() {
- if trace && Debug['x'] != 0 {
- defer p.trace("import_package")()
- }
-
- p.want(LPACKAGE)
- var name string
- if p.tok == LNAME {
- name = p.sym_.Name
- p.next()
- } else {
- p.import_error()
- }
-
- // read but skip "safe" bit (see issue #15772)
- if p.tok == LNAME {
- p.next()
- }
- p.want(';')
-
- if importpkg.Name == "" {
- importpkg.Name = name
- numImport[name]++
- } else if importpkg.Name != name {
- Yyerror("conflicting names %s and %s for package %q", importpkg.Name, name, importpkg.Path)
- }
-
- typecheckok = true
- defercheckwidth()
-
- p.hidden_import_list()
- p.want('$')
- // don't read past 2nd '$'
- if p.tok != '$' {
- p.import_error()
- }
-
- resumecheckwidth()
- typecheckok = false
-}
-
// Declaration = ConstDecl | TypeDecl | VarDecl .
// ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
// TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
p.next()
return x
- case LNAME, '@', '?':
+ case LNAME:
return p.name()
case '(':
case '.':
p.next()
switch p.tok {
- case LNAME, '@', '?':
+ case LNAME:
// pexpr '.' sym
x = p.new_dotname(x)
defer p.trace("onew_name")()
}
- switch p.tok {
- case LNAME, '@', '?':
+ if p.tok == LNAME {
return p.new_name(p.sym())
}
return nil
}
func (p *parser) sym() *Sym {
- switch p.tok {
- case LNAME:
+ if p.tok == LNAME {
s := p.sym_ // from localpkg
p.next()
- // during imports, unqualified non-exported identifiers are from builtinpkg
- if importpkg != nil && !exportname(s.Name) {
- s = Pkglookup(s.Name, builtinpkg)
- }
return s
-
- case '@':
- return p.hidden_importsym()
-
- case '?':
- p.next()
- return nil
-
- default:
- p.syntax_error("expecting name")
- p.advance()
- return new(Sym)
}
+
+ p.syntax_error("expecting name")
+ p.advance()
+ return new(Sym)
}
func mkname(sym *Sym) *Node {
p.next()
return Nod(OIND, p.ntype(), nil)
- case LNAME, '@', '?':
+ case LNAME:
return p.dotname()
case '(':
}
switch p.tok {
- case LNAME, '@', '?':
+ case LNAME:
// FunctionName Signature
name := p.sym()
t := p.signature(nil)
}
}
-func (p *parser) hidden_fndcl() *Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_fndcl")()
- }
-
- switch p.tok {
- default:
- // hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres
- s1 := p.hidden_pkg_importsym()
- p.want('(')
- s3 := p.ohidden_funarg_list()
- p.want(')')
- s5 := p.ohidden_funres()
-
- s := s1
- t := functype(nil, s3, s5)
-
- importsym(s, ONAME)
- if s.Def != nil && s.Def.Op == ONAME {
- if Eqtype(t, s.Def.Type) {
- dclcontext = PDISCARD // since we skip funchdr below
- return nil
- }
- Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", s, s.Def.Type, t)
- }
-
- ss := newfuncname(s)
- ss.Type = t
- declare(ss, PFUNC)
-
- funchdr(ss)
- return ss
-
- case '(':
- // '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres
- p.next()
- s2 := p.hidden_funarg_list()
- p.want(')')
- s4 := p.sym()
- p.want('(')
- s6 := p.ohidden_funarg_list()
- p.want(')')
- s8 := p.ohidden_funres()
-
- ss := methodname1(newname(s4), s2[0].Right)
- ss.Type = functype(s2[0], s6, s8)
-
- checkwidth(ss.Type)
- addmethod(s4, ss.Type, p.structpkg, false, p.pragma&Nointerface != 0)
- p.pragma = 0
- funchdr(ss)
-
- // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
- // (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled
- // out by typecheck's lookdot as this $$.ttype. So by providing
- // this back link here we avoid special casing there.
- ss.Type.SetNname(ss)
- return ss
- }
-}
-
// FunctionBody = Block .
func (p *parser) fnbody() []*Node {
if trace && Debug['x'] != 0 {
return []*Node{field}
}
- // LNAME belongs to first *Sym of new_name_list
- //
- // during imports, unqualified non-exported identifiers are from builtinpkg
- if importpkg != nil && !exportname(sym.Name) {
- sym = Pkglookup(sym.Name, builtinpkg)
- if sym == nil {
- p.import_error()
- }
- }
- fallthrough
-
- case '@', '?':
// new_name_list ntype oliteral
fields := p.new_name_list(sym)
typ := p.ntype()
ifacedcl(meth)
return meth
- case '@', '?':
- // MethodName Signature
- //
- // We arrive here when parsing an interface type declared inside
- // an exported and inlineable function and the interface declares
- // unexported methods (which are then package-qualified).
- //
- // Since the compiler always flattens embedded interfaces, we
- // will never see an embedded package-qualified interface in export
- // data; i.e., when we reach here we know it must be a method.
- //
- // See also issue 14164.
- mname := newname(p.sym())
- sig := p.signature(fakethis())
-
- meth := Nod(ODCLFIELD, mname, sig)
- ifacedcl(meth)
- return meth
-
case '(':
p.next()
pname := p.packname(nil)
}
switch p.tok {
- case LNAME, '@', '?':
- name = p.sym() // nil if p.tok == '?' (importing only)
+ case LNAME:
+ name = p.sym()
switch p.tok {
- case LCOMM, LFUNC, '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, '*', LNAME, '@', '?', '(':
+ case LCOMM, LFUNC, '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, '*', LNAME, '(':
// sym name_or_type
typ = p.ntype()
// explicit type: use type for earlier parameters
T = t
// an explicitly typed entry must have a name
- // TODO(gri) remove extra importpkg == nil check below
- // after switch to binary eport format
- // Exported inlined function bodies containing function
- // literals may print parameter names as '?' resulting
- // in nil *Sym and thus nil names. Don't report an error
- // in this case.
- if p.name == nil && importpkg == nil {
+ if p.name == nil {
T = nil // error
}
} else {
case LVAR, LCONST, LTYPE:
return liststmt(p.common_dcl())
- case LNAME, '@', '?', LLITERAL, LFUNC, '(', // operands
+ case LNAME, LLITERAL, LFUNC, '(', // operands
'[', LSTRUCT, LMAP, LCHAN, LINTERFACE, // composite types
'+', '-', '*', '&', '^', LCOMM, '!': // unary operators
return p.simple_stmt(true, false)
p.advance(follow)
return false
}
-
-// ----------------------------------------------------------------------------
-// Importing packages
-
-func (p *parser) import_error() {
- p.syntax_error("in export data of imported package")
- p.next()
-}
-
-// The methods below reflect a 1:1 translation of the original (and now defunct)
-// go.y yacc productions. They could be simplified significantly and also use better
-// variable names. However, we will be able to delete them once we enable the
-// new export format by default, so it's not worth the effort (issue 13241).
-
-func (p *parser) hidden_importsym() *Sym {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_importsym")()
- }
-
- p.want('@')
- var s2 Val
- if p.tok == LLITERAL {
- s2 = p.val
- p.next()
- } else {
- p.import_error()
- }
- p.want('.')
-
- switch p.tok {
- case LNAME:
- s4 := p.sym_
- p.next()
-
- var p *Pkg
-
- if s2.U.(string) == "" {
- p = importpkg
- } else {
- if isbadimport(s2.U.(string)) {
- errorexit()
- }
- p = mkpkg(s2.U.(string))
- }
- return Pkglookup(s4.Name, p)
-
- case '?':
- p.next()
-
- var p *Pkg
-
- if s2.U.(string) == "" {
- p = importpkg
- } else {
- if isbadimport(s2.U.(string)) {
- errorexit()
- }
- p = mkpkg(s2.U.(string))
- }
- return Pkglookup("?", p)
-
- default:
- p.import_error()
- return nil
- }
-}
-
-func (p *parser) ohidden_funarg_list() []*Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("ohidden_funarg_list")()
- }
-
- var ss []*Node
- if p.tok != ')' {
- ss = p.hidden_funarg_list()
- }
- return ss
-}
-
-func (p *parser) ohidden_structdcl_list() []*Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("ohidden_structdcl_list")()
- }
-
- var ss []*Node
- if p.tok != '}' {
- ss = p.hidden_structdcl_list()
- }
- return ss
-}
-
-func (p *parser) ohidden_interfacedcl_list() []*Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("ohidden_interfacedcl_list")()
- }
-
- var ss []*Node
- if p.tok != '}' {
- ss = p.hidden_interfacedcl_list()
- }
- return ss
-}
-
-// import syntax from package header
-func (p *parser) hidden_import() {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_import")()
- }
-
- switch p.tok {
- case LIMPORT:
- // LIMPORT LNAME LLITERAL ';'
- p.next()
- var s2 *Sym
- if p.tok == LNAME {
- s2 = p.sym_
- p.next()
- } else {
- p.import_error()
- }
- var s3 Val
- if p.tok == LLITERAL {
- s3 = p.val
- p.next()
- } else {
- p.import_error()
- }
- p.want(';')
-
- importimport(s2, s3.U.(string))
-
- case LVAR:
- // LVAR hidden_pkg_importsym hidden_type ';'
- p.next()
- s2 := p.hidden_pkg_importsym()
- s3 := p.hidden_type()
- p.want(';')
-
- importvar(s2, s3)
-
- case LCONST:
- // LCONST hidden_pkg_importsym '=' hidden_constant ';'
- // LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'
- p.next()
- s2 := p.hidden_pkg_importsym()
- var s3 *Type = Types[TIDEAL]
- if p.tok != '=' {
- s3 = p.hidden_type()
- }
- p.want('=')
- s4 := p.hidden_constant()
- p.want(';')
-
- importconst(s2, s3, s4)
-
- case LTYPE:
- // LTYPE hidden_pkgtype hidden_type ';'
- p.next()
- s2 := p.hidden_pkgtype()
- s3 := p.hidden_type()
- p.want(';')
-
- importtype(s2, s3)
-
- case LFUNC:
- // LFUNC hidden_fndcl fnbody ';'
- p.next()
- s2 := p.hidden_fndcl()
- s3 := p.fnbody()
- p.want(';')
-
- if s2 == nil {
- dclcontext = PEXTERN // since we skip the funcbody below
- return
- }
-
- s2.Func.Inl.Set(s3)
-
- funcbody(s2)
- importlist = append(importlist, s2)
-
- if Debug['E'] > 0 {
- fmt.Printf("import [%q] func %v \n", importpkg.Path, s2)
- if Debug['m'] > 2 && s2.Func.Inl.Len() != 0 {
- fmt.Printf("inl body:%v\n", s2.Func.Inl)
- }
- }
-
- default:
- p.import_error()
- }
-}
-
-func (p *parser) hidden_pkg_importsym() *Sym {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_pkg_importsym")()
- }
-
- s := p.hidden_importsym()
- p.structpkg = s.Pkg
- return s
-}
-
-func (p *parser) hidden_pkgtype() *Type {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_pkgtype")()
- }
-
- return pkgtype(p.hidden_pkg_importsym())
-}
-
-// ----------------------------------------------------------------------------
-// Importing types
-
-func (p *parser) hidden_type() *Type {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_type")()
- }
-
- switch p.tok {
- default:
- return p.hidden_type_misc()
- case LCOMM:
- return p.hidden_type_recv_chan()
- case LFUNC:
- return p.hidden_type_func()
- }
-}
-
-func (p *parser) hidden_type_non_recv_chan() *Type {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_type_non_recv_chan")()
- }
-
- switch p.tok {
- default:
- return p.hidden_type_misc()
- case LFUNC:
- return p.hidden_type_func()
- }
-}
-
-func (p *parser) hidden_type_misc() *Type {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_type_misc")()
- }
-
- switch p.tok {
- case '@':
- // hidden_importsym
- s1 := p.hidden_importsym()
- return pkgtype(s1)
-
- case LNAME:
- // LNAME
- s1 := p.sym_
- p.next()
-
- // predefined name like uint8
- s1 = Pkglookup(s1.Name, builtinpkg)
- if s1.Def == nil || s1.Def.Op != OTYPE {
- Yyerror("%s is not a type", s1.Name)
- return nil
- } else {
- return s1.Def.Type
- }
-
- case '[':
- // '[' ']' hidden_type
- // '[' LLITERAL ']' hidden_type
- p.next()
- var s2 *Node
- if p.tok == LLITERAL {
- s2 = nodlit(p.val)
- p.next()
- }
- p.want(']')
- s4 := p.hidden_type()
-
- return aindex(s2, s4)
-
- case LMAP:
- // LMAP '[' hidden_type ']' hidden_type
- p.next()
- p.want('[')
- s3 := p.hidden_type()
- p.want(']')
- s5 := p.hidden_type()
-
- return typMap(s3, s5)
-
- case LSTRUCT:
- // LSTRUCT '{' ohidden_structdcl_list '}'
- p.next()
- p.want('{')
- s3 := p.ohidden_structdcl_list()
- p.want('}')
-
- return tostruct(s3)
-
- case LINTERFACE:
- // LINTERFACE '{' ohidden_interfacedcl_list '}'
- p.next()
- p.want('{')
- s3 := p.ohidden_interfacedcl_list()
- p.want('}')
-
- return tointerface(s3)
-
- case '*':
- // '*' hidden_type
- p.next()
- s2 := p.hidden_type()
- return Ptrto(s2)
-
- case LCHAN:
- p.next()
- switch p.tok {
- default:
- // LCHAN hidden_type_non_recv_chan
- s2 := p.hidden_type_non_recv_chan()
- ss := typChan(s2, Cboth)
- return ss
-
- case '(':
- // LCHAN '(' hidden_type_recv_chan ')'
- p.next()
- s3 := p.hidden_type_recv_chan()
- p.want(')')
- ss := typChan(s3, Cboth)
- return ss
-
- case LCOMM:
- // LCHAN hidden_type
- p.next()
- s3 := p.hidden_type()
- ss := typChan(s3, Csend)
- return ss
- }
-
- default:
- p.import_error()
- return nil
- }
-}
-
-func (p *parser) hidden_type_recv_chan() *Type {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_type_recv_chan")()
- }
-
- p.want(LCOMM)
- p.want(LCHAN)
- s3 := p.hidden_type()
-
- ss := typChan(s3, Crecv)
- return ss
-}
-
-func (p *parser) hidden_type_func() *Type {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_type_func")()
- }
-
- p.want(LFUNC)
- p.want('(')
- s3 := p.ohidden_funarg_list()
- p.want(')')
- s5 := p.ohidden_funres()
-
- return functype(nil, s3, s5)
-}
-
-func (p *parser) hidden_funarg() *Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_funarg")()
- }
-
- s1 := p.sym()
- switch p.tok {
- default:
- s2 := p.hidden_type()
- s3 := p.oliteral()
-
- ss := Nod(ODCLFIELD, nil, typenod(s2))
- if s1 != nil {
- ss.Left = newname(s1)
- }
- ss.SetVal(s3)
- return ss
-
- case LDDD:
- p.next()
- s3 := p.hidden_type()
- s4 := p.oliteral()
-
- t := typSlice(s3)
-
- ss := Nod(ODCLFIELD, nil, typenod(t))
- if s1 != nil {
- ss.Left = newname(s1)
- }
- ss.Isddd = true
- ss.SetVal(s4)
-
- return ss
- }
-}
-
-func (p *parser) hidden_structdcl() *Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_structdcl")()
- }
-
- s1 := p.sym()
- s2 := p.hidden_type()
- s3 := p.oliteral()
-
- var ss *Node
- if s1 != nil && s1.Name != "?" {
- ss = Nod(ODCLFIELD, newname(s1), typenod(s2))
- ss.SetVal(s3)
- } else {
- s := s2.Sym
- if s == nil && s2.IsPtr() {
- s = s2.Elem().Sym
- }
- pkg := importpkg
- if s1 != nil {
- pkg = s1.Pkg
- }
- ss = embedded(s, pkg)
- ss.Right = typenod(s2)
- ss.SetVal(s3)
- }
-
- return ss
-}
-
-func (p *parser) hidden_interfacedcl() *Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_interfacedcl")()
- }
-
- // The original (now defunct) grammar in go.y accepted both a method
- // or an (embedded) type:
- //
- // hidden_interfacedcl:
- // sym '(' ohidden_funarg_list ')' ohidden_funres
- // {
- // $$ = Nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
- // }
- // | hidden_type
- // {
- // $$ = Nod(ODCLFIELD, nil, typenod($1));
- // }
- //
- // But the current textual export code only exports (inlined) methods,
- // even if the methods came from embedded interfaces. Furthermore, in
- // the original grammar, hidden_type may also start with a sym (LNAME
- // or '@'), complicating matters further. Since we never have embedded
- // types, only parse methods here.
-
- s1 := p.sym()
- p.want('(')
- s3 := p.ohidden_funarg_list()
- p.want(')')
- s5 := p.ohidden_funres()
-
- return Nod(ODCLFIELD, newname(s1), typenod(functype(fakethis(), s3, s5)))
-}
-
-func (p *parser) ohidden_funres() []*Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("ohidden_funres")()
- }
-
- switch p.tok {
- default:
- return nil
-
- case '(', '@', LNAME, '[', LMAP, LSTRUCT, LINTERFACE, '*', LCHAN, LCOMM, LFUNC:
- return p.hidden_funres()
- }
-}
-
-func (p *parser) hidden_funres() []*Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_funres")()
- }
-
- switch p.tok {
- case '(':
- p.next()
- s2 := p.ohidden_funarg_list()
- p.want(')')
- return s2
-
- default:
- s1 := p.hidden_type()
- return []*Node{Nod(ODCLFIELD, nil, typenod(s1))}
- }
-}
-
-// ----------------------------------------------------------------------------
-// Importing constants
-
-func (p *parser) hidden_literal() *Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_literal")()
- }
-
- switch p.tok {
- case LLITERAL:
- ss := nodlit(p.val)
- p.next()
- return ss
-
- case '-':
- p.next()
- if p.tok == LLITERAL {
- ss := nodlit(p.val)
- p.next()
- switch u := ss.Val().U.(type) {
- case *Mpint:
- u.Neg()
- case *Mpflt:
- u.Neg()
- case *Mpcplx:
- u.Real.Neg()
- u.Imag.Neg()
- default:
- Yyerror("bad negated constant")
- }
- return ss
- } else {
- p.import_error()
- return nil
- }
-
- case LNAME, '@', '?':
- s1 := p.sym()
- ss := oldname(Pkglookup(s1.Name, builtinpkg))
- if ss.Op != OLITERAL {
- Yyerror("bad constant %v", ss.Sym)
- }
- return ss
-
- default:
- p.import_error()
- return nil
- }
-}
-
-func (p *parser) hidden_constant() *Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_constant")()
- }
-
- switch p.tok {
- default:
- return p.hidden_literal()
- case '(':
- p.next()
- s2 := p.hidden_literal()
- p.want('+')
- s4 := p.hidden_literal()
- p.want(')')
-
- if s2.Val().Ctype() == CTRUNE && s4.Val().Ctype() == CTINT {
- ss := s2
- s2.Val().U.(*Mpint).Add(s4.Val().U.(*Mpint))
- return ss
- }
- s4.Val().U.(*Mpcplx).Real = s4.Val().U.(*Mpcplx).Imag
- s4.Val().U.(*Mpcplx).Imag.SetFloat64(0.0)
- return nodcplxlit(s2.Val(), s4.Val())
- }
-}
-
-func (p *parser) hidden_import_list() {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_import_list")()
- }
-
- for p.tok != '$' {
- p.hidden_import()
- }
-}
-
-func (p *parser) hidden_funarg_list() []*Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_funarg_list")()
- }
-
- s1 := p.hidden_funarg()
- ss := []*Node{s1}
- for p.got(',') {
- s3 := p.hidden_funarg()
- ss = append(ss, s3)
- }
- return ss
-}
-
-func (p *parser) hidden_structdcl_list() []*Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_structdcl_list")()
- }
-
- s1 := p.hidden_structdcl()
- ss := []*Node{s1}
- for p.got(';') {
- s3 := p.hidden_structdcl()
- ss = append(ss, s3)
- }
- return ss
-}
-
-func (p *parser) hidden_interfacedcl_list() []*Node {
- if trace && Debug['x'] != 0 {
- defer p.trace("hidden_interfacedcl_list")()
- }
-
- s1 := p.hidden_interfacedcl()
- ss := []*Node{s1}
- for p.got(';') {
- s3 := p.hidden_interfacedcl()
- ss = append(ss, s3)
- }
- return ss
-}
import (
"bufio"
- "errors"
"fmt"
"go/build"
- "go/token"
- "io"
+ "go/types"
"io/ioutil"
"os"
"path/filepath"
- "sort"
- "strconv"
"strings"
- "text/scanner"
-
- exact "go/constant"
- "go/types"
)
// debugging/development support
return
}
-// ImportData imports a package by reading the gc-generated export data,
-// adds the corresponding package object to the packages map indexed by id,
-// and returns the object.
-//
-// The packages map must contains all packages already imported. The data
-// reader position must be the beginning of the export data section. The
-// filename is only used in error messages.
-//
-// If packages[id] contains the completely imported package, that package
-// can be used directly, and there is no need to call this function (but
-// there is also no harm but for extra time used).
-//
-func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) {
- // support for parser error handling
- defer func() {
- switch r := recover().(type) {
- case nil:
- // nothing to do
- case importError:
- err = r
- default:
- panic(r) // internal error
- }
- }()
-
- var p parser
- p.init(filename, id, data, packages)
- pkg = p.parseExport()
-
- return
-}
-
// Import imports a gc-generated package given its import path and srcDir, adds
// the corresponding package object to the packages map, and returns the object.
// The packages map must contain all packages already imported.
switch hdr {
case "$$\n":
- return ImportData(packages, filename, id, buf)
+ err = fmt.Errorf("cannot import %s: old export format no longer supported (recompile library)", path)
case "$$B\n":
var data []byte
data, err = ioutil.ReadAll(buf)
return
}
-// ----------------------------------------------------------------------------
-// Parser
-
-// TODO(gri) Imported objects don't have position information.
-// Ideally use the debug table line info; alternatively
-// create some fake position (or the position of the
-// import). That way error messages referring to imported
-// objects can print meaningful information.
-
-// parser parses the exports inside a gc compiler-produced
-// object/archive file and populates its scope with the results.
-type parser struct {
- scanner scanner.Scanner
- tok rune // current token
- lit string // literal string; only valid for Ident, Int, String tokens
- id string // package id of imported package
- sharedPkgs map[string]*types.Package // package id -> package object (across importer)
- localPkgs map[string]*types.Package // package id -> package object (just this package)
-}
-
-func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) {
- p.scanner.Init(src)
- p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
- p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | 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.sharedPkgs = packages
- if debug {
- // check consistency of packages map
- for _, pkg := range packages {
- if pkg.Name() == "" {
- fmt.Printf("no package name for %s\n", pkg.Path())
- }
- }
- }
-}
-
-func (p *parser) next() {
- p.tok = p.scanner.Scan()
- switch p.tok {
- case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
- p.lit = p.scanner.TokenText()
- default:
- p.lit = ""
- }
- if debug {
- fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
- }
-}
-
-func declTypeName(pkg *types.Package, name string) *types.TypeName {
- scope := pkg.Scope()
- if obj := scope.Lookup(name); obj != nil {
- return obj.(*types.TypeName)
- }
- obj := types.NewTypeName(token.NoPos, pkg, name, nil)
- // a named type may be referred to before the underlying type
- // is known - set it up
- types.NewNamed(obj, nil, nil)
- scope.Insert(obj)
- return obj
-}
-
-// ----------------------------------------------------------------------------
-// Error handling
-
-// Internal errors are boxed as importErrors.
-type importError struct {
- pos scanner.Position
- err error
-}
-
-func (e importError) Error() string {
- return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
-}
-
-func (p *parser) error(err interface{}) {
- if s, ok := err.(string); ok {
- err = errors.New(s)
- }
- // panic with a runtime.Error if err is not an error
- panic(importError{p.scanner.Pos(), err.(error)})
-}
-
-func (p *parser) errorf(format string, args ...interface{}) {
- p.error(fmt.Sprintf(format, args...))
-}
-
-func (p *parser) expect(tok rune) string {
- lit := p.lit
- if p.tok != tok {
- p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
- }
- p.next()
- return lit
-}
-
-func (p *parser) expectSpecial(tok string) {
- sep := 'x' // not white space
- i := 0
- for i < len(tok) && p.tok == rune(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 *parser) expectKeyword(keyword string) {
- lit := p.expect(scanner.Ident)
- if lit != keyword {
- p.errorf("expected keyword %s, got %q", keyword, lit)
- }
-}
-
-// ----------------------------------------------------------------------------
-// Qualified and unqualified names
-
-// PackageId = string_lit .
-//
-func (p *parser) parsePackageId() string {
- id, err := strconv.Unquote(p.expect(scanner.String))
- if err != nil {
- p.error(err)
- }
- // id == "" stands for the imported package id
- // (only known at time of package installation)
- if id == "" {
- id = p.id
- }
- return id
-}
-
-// PackageName = ident .
-//
-func (p *parser) parsePackageName() string {
- return p.expect(scanner.Ident)
-}
-
-// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
-func (p *parser) 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
-}
-
-// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
-//
-func (p *parser) parseQualifiedName() (id, name string) {
- p.expect('@')
- id = p.parsePackageId()
- p.expect('.')
- // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
- if p.tok == '?' {
- p.next()
- } else {
- name = p.parseDotIdent()
- }
- return
-}
-
-// getPkg returns the package for a given id. If the package is
-// not found, create the package and add it to the p.localPkgs
-// and p.sharedPkgs maps. name is the (expected) name of the
-// package. If name == "", the package name is expected to be
-// set later via an import clause in the export data.
-//
-// id identifies a package, usually by a canonical package path like
-// "encoding/json" but possibly by a non-canonical import path like
-// "./json".
-//
-func (p *parser) getPkg(id, name string) *types.Package {
- // package unsafe is not in the packages maps - handle explicitly
- if id == "unsafe" {
- return types.Unsafe
- }
-
- pkg := p.localPkgs[id]
- if pkg == nil {
- // first import of id from this package
- pkg = p.sharedPkgs[id]
- if pkg == nil {
- // first import of id by this importer;
- // add (possibly unnamed) pkg to shared packages
- pkg = types.NewPackage(id, name)
- p.sharedPkgs[id] = pkg
- }
- // add (possibly unnamed) pkg to local packages
- if p.localPkgs == nil {
- p.localPkgs = make(map[string]*types.Package)
- }
- p.localPkgs[id] = pkg
- } else if name != "" {
- // package exists already and we have an expected package name;
- // make sure names match or set package name if necessary
- if pname := pkg.Name(); pname == "" {
- pkg.SetName(name)
- } else if pname != name {
- p.errorf("%s package name mismatch: %s (given) vs %s (expected)", id, pname, name)
- }
- }
- return pkg
-}
-
-// parseExportedName is like parseQualifiedName, but
-// the package id is resolved to an imported *types.Package.
-//
-func (p *parser) parseExportedName() (pkg *types.Package, name string) {
- id, name := p.parseQualifiedName()
- pkg = p.getPkg(id, "")
- return
-}
-
-// ----------------------------------------------------------------------------
-// Types
-
-// BasicType = identifier .
-//
-func (p *parser) parseBasicType() types.Type {
- id := p.expect(scanner.Ident)
- obj := types.Universe.Lookup(id)
- if obj, ok := obj.(*types.TypeName); ok {
- return obj.Type()
- }
- p.errorf("not a basic type: %s", id)
- return nil
-}
-
-// ArrayType = "[" int_lit "]" Type .
-//
-func (p *parser) parseArrayType(parent *types.Package) types.Type {
- // "[" already consumed and lookahead known not to be "]"
- lit := p.expect(scanner.Int)
- p.expect(']')
- elem := p.parseType(parent)
- n, err := strconv.ParseInt(lit, 10, 64)
- if err != nil {
- p.error(err)
- }
- return types.NewArray(elem, n)
-}
-
-// MapType = "map" "[" Type "]" Type .
-//
-func (p *parser) parseMapType(parent *types.Package) types.Type {
- p.expectKeyword("map")
- p.expect('[')
- key := p.parseType(parent)
- p.expect(']')
- elem := p.parseType(parent)
- return types.NewMap(key, elem)
-}
-
-// Name = identifier | "?" | QualifiedName .
-//
-// For unqualified and anonymous names, the returned package is the parent
-// package unless parent == nil, in which case the returned package is the
-// package being imported. (The parent package is not nil if the the name
-// is an unqualified struct field or interface method name belonging to a
-// type declared in another package.)
-//
-// For qualified names, the returned package is nil (and not created if
-// it doesn't exist yet) unless materializePkg is set (which creates an
-// unnamed package with valid package path). In the latter case, a
-// subsequent import clause is expected to provide a name for the package.
-//
-func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) {
- pkg = parent
- if pkg == nil {
- pkg = p.sharedPkgs[p.id]
- }
- switch p.tok {
- case scanner.Ident:
- name = p.lit
- p.next()
- case '?':
- // anonymous
- p.next()
- case '@':
- // exported name prefixed with package path
- pkg = nil
- var id string
- id, name = p.parseQualifiedName()
- if materializePkg {
- pkg = p.getPkg(id, "")
- }
- default:
- p.error("name expected")
- }
- return
-}
-
func deref(typ types.Type) types.Type {
if p, _ := typ.(*types.Pointer); p != nil {
return p.Elem()
return typ
}
-// Field = Name Type [ string_lit ] .
-//
-func (p *parser) parseField(parent *types.Package) (*types.Var, string) {
- pkg, name := p.parseName(parent, true)
- typ := p.parseType(parent)
- anonymous := false
- if name == "" {
- // anonymous field - typ must be T or *T and T must be a type name
- switch typ := deref(typ).(type) {
- case *types.Basic: // basic types are named types
- pkg = nil // objects defined in Universe scope have no package
- name = typ.Name()
- case *types.Named:
- name = typ.Obj().Name()
- default:
- p.errorf("anonymous field expected")
- }
- anonymous = true
- }
- tag := ""
- if p.tok == scanner.String {
- s := p.expect(scanner.String)
- var err error
- tag, err = strconv.Unquote(s)
- if err != nil {
- p.errorf("invalid struct tag %s: %s", s, err)
- }
- }
- return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag
-}
-
-// StructType = "struct" "{" [ FieldList ] "}" .
-// FieldList = Field { ";" Field } .
-//
-func (p *parser) parseStructType(parent *types.Package) types.Type {
- var fields []*types.Var
- var tags []string
-
- p.expectKeyword("struct")
- p.expect('{')
- for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
- if i > 0 {
- p.expect(';')
- }
- fld, tag := p.parseField(parent)
- if tag != "" && tags == nil {
- tags = make([]string, i)
- }
- if tags != nil {
- tags = append(tags, tag)
- }
- fields = append(fields, fld)
- }
- p.expect('}')
-
- return types.NewStruct(fields, tags)
-}
-
-// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
-//
-func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
- _, name := p.parseName(nil, false)
- // remove gc-specific parameter numbering
- if i := strings.Index(name, "·"); i >= 0 {
- name = name[:i]
- }
- if p.tok == '.' {
- p.expectSpecial("...")
- isVariadic = true
- }
- typ := p.parseType(nil)
- if isVariadic {
- typ = types.NewSlice(typ)
- }
- // ignore argument tag (e.g. "noescape")
- if p.tok == scanner.String {
- p.next()
- }
- // TODO(gri) should we provide a package?
- par = types.NewVar(token.NoPos, nil, name, typ)
- return
-}
-
-// Parameters = "(" [ ParameterList ] ")" .
-// ParameterList = { Parameter "," } Parameter .
-//
-func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
- p.expect('(')
- for p.tok != ')' && p.tok != scanner.EOF {
- if len(list) > 0 {
- p.expect(',')
- }
- par, variadic := p.parseParameter()
- list = append(list, par)
- if variadic {
- if isVariadic {
- p.error("... not on final argument")
- }
- isVariadic = true
- }
- }
- p.expect(')')
-
- return
-}
-
-// Signature = Parameters [ Result ] .
-// Result = Type | Parameters .
-//
-func (p *parser) parseSignature(recv *types.Var) *types.Signature {
- params, isVariadic := p.parseParameters()
-
- // optional result type
- var results []*types.Var
- if p.tok == '(' {
- var variadic bool
- results, variadic = p.parseParameters()
- if variadic {
- p.error("... not permitted on result type")
- }
- }
-
- return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic)
-}
-
-// InterfaceType = "interface" "{" [ MethodList ] "}" .
-// MethodList = Method { ";" Method } .
-// Method = Name Signature .
-//
-// The methods of embedded interfaces are always "inlined"
-// by the compiler and thus embedded interfaces are never
-// visible in the export data.
-//
-func (p *parser) parseInterfaceType(parent *types.Package) types.Type {
- var methods []*types.Func
-
- p.expectKeyword("interface")
- p.expect('{')
- for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
- if i > 0 {
- p.expect(';')
- }
- pkg, name := p.parseName(parent, true)
- sig := p.parseSignature(nil)
- methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
- }
- p.expect('}')
-
- // Complete requires the type's embedded interfaces to be fully defined,
- // but we do not define any
- return types.NewInterface(methods, nil).Complete()
-}
-
-// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
-//
-func (p *parser) parseChanType(parent *types.Package) types.Type {
- dir := types.SendRecv
- if p.tok == scanner.Ident {
- p.expectKeyword("chan")
- if p.tok == '<' {
- p.expectSpecial("<-")
- dir = types.SendOnly
- }
- } else {
- p.expectSpecial("<-")
- p.expectKeyword("chan")
- dir = types.RecvOnly
- }
- elem := p.parseType(parent)
- return types.NewChan(dir, elem)
-}
-
-// Type =
-// BasicType | TypeName | ArrayType | SliceType | StructType |
-// PointerType | FuncType | InterfaceType | MapType | ChanType |
-// "(" Type ")" .
-//
-// BasicType = ident .
-// TypeName = ExportedName .
-// SliceType = "[" "]" Type .
-// PointerType = "*" Type .
-// FuncType = "func" Signature .
-//
-func (p *parser) parseType(parent *types.Package) types.Type {
- switch p.tok {
- case scanner.Ident:
- switch p.lit {
- default:
- return p.parseBasicType()
- case "struct":
- return p.parseStructType(parent)
- case "func":
- // FuncType
- p.next()
- return p.parseSignature(nil)
- case "interface":
- return p.parseInterfaceType(parent)
- case "map":
- return p.parseMapType(parent)
- case "chan":
- return p.parseChanType(parent)
- }
- case '@':
- // TypeName
- pkg, name := p.parseExportedName()
- return declTypeName(pkg, name).Type()
- case '[':
- p.next() // look ahead
- if p.tok == ']' {
- // SliceType
- p.next()
- return types.NewSlice(p.parseType(parent))
- }
- return p.parseArrayType(parent)
- case '*':
- // PointerType
- p.next()
- return types.NewPointer(p.parseType(parent))
- case '<':
- return p.parseChanType(parent)
- case '(':
- // "(" Type ")"
- p.next()
- typ := p.parseType(parent)
- p.expect(')')
- return typ
- }
- p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
- return nil
-}
-
-// ----------------------------------------------------------------------------
-// Declarations
-
-// ImportDecl = "import" PackageName PackageId .
-//
-func (p *parser) parseImportDecl() {
- p.expectKeyword("import")
- name := p.parsePackageName()
- p.getPkg(p.parsePackageId(), name)
-}
-
-// int_lit = [ "+" | "-" ] { "0" ... "9" } .
-//
-func (p *parser) parseInt() string {
- s := ""
- switch p.tok {
- case '-':
- s = "-"
- p.next()
- case '+':
- p.next()
- }
- return s + p.expect(scanner.Int)
-}
-
-// number = int_lit [ "p" int_lit ] .
-//
-func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
- // mantissa
- mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0)
- if mant == nil {
- panic("invalid mantissa")
- }
-
- if p.lit == "p" {
- // exponent (base 2)
- p.next()
- exp, err := strconv.ParseInt(p.parseInt(), 10, 0)
- if err != nil {
- p.error(err)
- }
- if exp < 0 {
- denom := exact.MakeInt64(1)
- denom = exact.Shift(denom, token.SHL, uint(-exp))
- typ = types.Typ[types.UntypedFloat]
- val = exact.BinaryOp(mant, token.QUO, denom)
- return
- }
- if exp > 0 {
- mant = exact.Shift(mant, token.SHL, uint(exp))
- }
- typ = types.Typ[types.UntypedFloat]
- val = mant
- return
- }
-
- typ = types.Typ[types.UntypedInt]
- val = mant
- return
-}
-
-// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
-// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
-// bool_lit = "true" | "false" .
-// complex_lit = "(" float_lit "+" float_lit "i" ")" .
-// rune_lit = "(" int_lit "+" int_lit ")" .
-// string_lit = `"` { unicode_char } `"` .
-//
-func (p *parser) parseConstDecl() {
- p.expectKeyword("const")
- pkg, name := p.parseExportedName()
-
- var typ0 types.Type
- if p.tok != '=' {
- // constant types are never structured - no need for parent type
- typ0 = p.parseType(nil)
- }
-
- p.expect('=')
- var typ types.Type
- var val exact.Value
- switch p.tok {
- case scanner.Ident:
- // bool_lit
- if p.lit != "true" && p.lit != "false" {
- p.error("expected true or false")
- }
- typ = types.Typ[types.UntypedBool]
- val = exact.MakeBool(p.lit == "true")
- p.next()
-
- case '-', scanner.Int:
- // int_lit
- typ, val = p.parseNumber()
-
- case '(':
- // complex_lit or rune_lit
- p.next()
- if p.tok == scanner.Char {
- p.next()
- p.expect('+')
- typ = types.Typ[types.UntypedRune]
- _, val = p.parseNumber()
- p.expect(')')
- break
- }
- _, re := p.parseNumber()
- p.expect('+')
- _, im := p.parseNumber()
- p.expectKeyword("i")
- p.expect(')')
- typ = types.Typ[types.UntypedComplex]
- val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
-
- case scanner.Char:
- // rune_lit
- typ = types.Typ[types.UntypedRune]
- val = exact.MakeFromLiteral(p.lit, token.CHAR, 0)
- p.next()
-
- case scanner.String:
- // string_lit
- typ = types.Typ[types.UntypedString]
- val = exact.MakeFromLiteral(p.lit, token.STRING, 0)
- p.next()
-
- default:
- p.errorf("expected literal got %s", scanner.TokenString(p.tok))
- }
-
- if typ0 == nil {
- typ0 = typ
- }
-
- pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val))
-}
-
-// TypeDecl = "type" ExportedName Type .
-//
-func (p *parser) parseTypeDecl() {
- p.expectKeyword("type")
- pkg, name := p.parseExportedName()
- obj := declTypeName(pkg, name)
-
- // The type object may have been imported before and thus already
- // have a type associated with it. We still need to parse the type
- // structure, but throw it away if the object already has a type.
- // This ensures that all imports refer to the same type object for
- // a given type declaration.
- typ := p.parseType(pkg)
-
- if name := obj.Type().(*types.Named); name.Underlying() == nil {
- name.SetUnderlying(typ)
- }
-}
-
-// VarDecl = "var" ExportedName Type .
-//
-func (p *parser) parseVarDecl() {
- p.expectKeyword("var")
- pkg, name := p.parseExportedName()
- typ := p.parseType(pkg)
- pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
-}
-
-// Func = Signature [ Body ] .
-// Body = "{" ... "}" .
-//
-func (p *parser) parseFunc(recv *types.Var) *types.Signature {
- sig := p.parseSignature(recv)
- if p.tok == '{' {
- p.next()
- for i := 1; i > 0; p.next() {
- switch p.tok {
- case '{':
- i++
- case '}':
- i--
- }
- }
- }
- return sig
-}
-
-// MethodDecl = "func" Receiver Name Func .
-// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
-//
-func (p *parser) parseMethodDecl() {
- // "func" already consumed
- p.expect('(')
- recv, _ := p.parseParameter() // receiver
- p.expect(')')
-
- // determine receiver base type object
- base := deref(recv.Type()).(*types.Named)
-
- // parse method name, signature, and possibly inlined body
- _, name := p.parseName(nil, false)
- sig := p.parseFunc(recv)
-
- // methods always belong to the same package as the base type object
- pkg := base.Obj().Pkg()
-
- // add method to type unless type was imported before
- // and method exists already
- // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
- base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
-}
-
-// FuncDecl = "func" ExportedName Func .
-//
-func (p *parser) parseFuncDecl() {
- // "func" already consumed
- pkg, name := p.parseExportedName()
- typ := p.parseFunc(nil)
- pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
-}
-
-// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
-//
-func (p *parser) parseDecl() {
- if p.tok == scanner.Ident {
- 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" PackageName [ "safe" ] "\n" .
-//
-func (p *parser) parseExport() *types.Package {
- p.expectKeyword("package")
- name := p.parsePackageName()
- if p.tok == scanner.Ident && p.lit == "safe" {
- // package was compiled with -u option - ignore
- p.next()
- }
- p.expect('\n')
-
- pkg := p.getPkg(p.id, name)
-
- 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)
- }
-
- // Record all locally referenced packages as imports.
- var imports []*types.Package
- for id, pkg2 := range p.localPkgs {
- if pkg2.Name() == "" {
- p.errorf("%s package has no name", id)
- }
- if id == p.id {
- continue // avoid self-edge
- }
- imports = append(imports, pkg2)
- }
- sort.Sort(byPath(imports))
- pkg.SetImports(imports)
-
- // package was imported completely and without errors
- pkg.MarkComplete()
-
- return pkg
-}
-
type byPath []*types.Package
func (a byPath) Len() int { return len(a) }
return filepath.Join(dirname, filename[:len(filename)-2]+"o")
}
-// TODO(gri) Remove this function once we switched to new export format by default.
-func compileNewExport(t *testing.T, dirname, filename string) string {
- testenv.MustHaveGoBuild(t)
- cmd := exec.Command("go", "tool", "compile", "-newexport", filename)
- cmd.Dir = dirname
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Logf("%s", out)
- t.Fatalf("go tool compile %s failed: %s", filename, err)
- }
- // filename should end with ".go"
- return filepath.Join(dirname, filename[:len(filename)-2]+"o")
-}
-
func testPath(t *testing.T, path, srcDir string) *types.Package {
t0 := time.Now()
pkg, err := Import(make(map[string]*types.Package), path, srcDir)
// additional packages that are not strictly required for
// import processing alone (they are exported to err "on
// the safe side").
+ // TODO(gri) update the want list to be precise, now that
+ // the textual export data is gone.
got := fmt.Sprint(pkg.Imports())
for _, want := range []string{"go/ast", "go/token"} {
if !strings.Contains(got, want) {
}
}
-// TODO(gri) Remove this function once we switched to new export format by default
-// (and update the comment and want list in TestImportTestdata).
-func TestImportTestdataNewExport(t *testing.T) {
- // This package only handles gc export data.
- if runtime.Compiler != "gc" {
- t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
- return
- }
-
- if outFn := compileNewExport(t, "testdata", "exports.go"); outFn != "" {
- defer os.Remove(outFn)
- }
-
- if pkg := testPath(t, "./testdata/exports", "."); pkg != nil {
- // The package's Imports list must include all packages
- // explicitly imported by exports.go, plus all packages
- // referenced indirectly via exported objects in exports.go.
- want := `[package ast ("go/ast") package token ("go/token")]`
- got := fmt.Sprint(pkg.Imports())
- if got != want {
- t.Errorf(`Package("exports").Imports() = %s, want %s`, got, want)
- }
- }
-}
-
func TestImportStdLib(t *testing.T) {
skipSpecialPlatforms(t)