[chore]: Bump github.com/miekg/dns from 1.1.55 to 1.1.56 (#2204)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
ba0a31d224
commit
f302ebb8e5
6
go.mod
6
go.mod
|
@ -35,7 +35,7 @@ require (
|
||||||
github.com/jackc/pgconn v1.14.1
|
github.com/jackc/pgconn v1.14.1
|
||||||
github.com/jackc/pgx/v5 v5.4.3
|
github.com/jackc/pgx/v5 v5.4.3
|
||||||
github.com/microcosm-cc/bluemonday v1.0.25
|
github.com/microcosm-cc/bluemonday v1.0.25
|
||||||
github.com/miekg/dns v1.1.55
|
github.com/miekg/dns v1.1.56
|
||||||
github.com/minio/minio-go/v7 v7.0.63
|
github.com/minio/minio-go/v7 v7.0.63
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
|
@ -164,9 +164,9 @@ require (
|
||||||
go.opentelemetry.io/otel/metric v1.17.0 // indirect
|
go.opentelemetry.io/otel/metric v1.17.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/mod v0.10.0 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.12.0 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.13.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
|
|
13
go.sum
13
go.sum
|
@ -431,8 +431,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
|
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
|
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
|
||||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
|
||||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
|
||||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ=
|
github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ=
|
||||||
|
@ -714,8 +714,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -779,8 +779,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -910,8 +910,9 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
@ -81,6 +81,7 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
||||||
* https://addr.tools/
|
* https://addr.tools/
|
||||||
* https://dnscheck.tools/
|
* https://dnscheck.tools/
|
||||||
* https://github.com/egbakou/domainverifier
|
* https://github.com/egbakou/domainverifier
|
||||||
|
* https://github.com/semihalev/sdns
|
||||||
|
|
||||||
|
|
||||||
Send pull request if you want to be listed here.
|
Send pull request if you want to be listed here.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
const hexDigit = "0123456789abcdef"
|
const hexDigit = "0123456789abcdef"
|
||||||
|
@ -330,8 +331,18 @@ func Fqdn(s string) string {
|
||||||
|
|
||||||
// CanonicalName returns the domain name in canonical form. A name in canonical
|
// CanonicalName returns the domain name in canonical form. A name in canonical
|
||||||
// form is lowercase and fully qualified. See Section 6.2 in RFC 4034.
|
// form is lowercase and fully qualified. See Section 6.2 in RFC 4034.
|
||||||
|
// According to the RFC all uppercase US-ASCII letters in the owner name of the
|
||||||
|
// RR areeplaced by the corresponding lowercase US-ASCII letters.
|
||||||
func CanonicalName(s string) string {
|
func CanonicalName(s string) string {
|
||||||
return strings.ToLower(Fqdn(s))
|
var result strings.Builder
|
||||||
|
for _, ch := range s {
|
||||||
|
if unicode.IsUpper(ch) && (ch >= 0x00 && ch <= 0x7F) {
|
||||||
|
result.WriteRune(unicode.ToLower(ch))
|
||||||
|
} else {
|
||||||
|
result.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Fqdn(result.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copied from the official Go code.
|
// Copied from the official Go code.
|
||||||
|
|
|
@ -896,23 +896,38 @@ func (dns *Msg) String() string {
|
||||||
return "<nil> MsgHdr"
|
return "<nil> MsgHdr"
|
||||||
}
|
}
|
||||||
s := dns.MsgHdr.String() + " "
|
s := dns.MsgHdr.String() + " "
|
||||||
s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", "
|
if dns.MsgHdr.Opcode == OpcodeUpdate {
|
||||||
s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", "
|
s += "ZONE: " + strconv.Itoa(len(dns.Question)) + ", "
|
||||||
s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", "
|
s += "PREREQ: " + strconv.Itoa(len(dns.Answer)) + ", "
|
||||||
s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n"
|
s += "UPDATE: " + strconv.Itoa(len(dns.Ns)) + ", "
|
||||||
|
s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n"
|
||||||
|
} else {
|
||||||
|
s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", "
|
||||||
|
s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", "
|
||||||
|
s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", "
|
||||||
|
s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n"
|
||||||
|
}
|
||||||
opt := dns.IsEdns0()
|
opt := dns.IsEdns0()
|
||||||
if opt != nil {
|
if opt != nil {
|
||||||
// OPT PSEUDOSECTION
|
// OPT PSEUDOSECTION
|
||||||
s += opt.String() + "\n"
|
s += opt.String() + "\n"
|
||||||
}
|
}
|
||||||
if len(dns.Question) > 0 {
|
if len(dns.Question) > 0 {
|
||||||
s += "\n;; QUESTION SECTION:\n"
|
if dns.MsgHdr.Opcode == OpcodeUpdate {
|
||||||
|
s += "\n;; ZONE SECTION:\n"
|
||||||
|
} else {
|
||||||
|
s += "\n;; QUESTION SECTION:\n"
|
||||||
|
}
|
||||||
for _, r := range dns.Question {
|
for _, r := range dns.Question {
|
||||||
s += r.String() + "\n"
|
s += r.String() + "\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(dns.Answer) > 0 {
|
if len(dns.Answer) > 0 {
|
||||||
s += "\n;; ANSWER SECTION:\n"
|
if dns.MsgHdr.Opcode == OpcodeUpdate {
|
||||||
|
s += "\n;; PREREQUISITE SECTION:\n"
|
||||||
|
} else {
|
||||||
|
s += "\n;; ANSWER SECTION:\n"
|
||||||
|
}
|
||||||
for _, r := range dns.Answer {
|
for _, r := range dns.Answer {
|
||||||
if r != nil {
|
if r != nil {
|
||||||
s += r.String() + "\n"
|
s += r.String() + "\n"
|
||||||
|
@ -920,7 +935,11 @@ func (dns *Msg) String() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(dns.Ns) > 0 {
|
if len(dns.Ns) > 0 {
|
||||||
s += "\n;; AUTHORITY SECTION:\n"
|
if dns.MsgHdr.Opcode == OpcodeUpdate {
|
||||||
|
s += "\n;; UPDATE SECTION:\n"
|
||||||
|
} else {
|
||||||
|
s += "\n;; AUTHORITY SECTION:\n"
|
||||||
|
}
|
||||||
for _, r := range dns.Ns {
|
for _, r := range dns.Ns {
|
||||||
if r != nil {
|
if r != nil {
|
||||||
s += r.String() + "\n"
|
s += r.String() + "\n"
|
||||||
|
|
|
@ -236,6 +236,9 @@ var CertTypeToString = map[uint16]string{
|
||||||
CertOID: "OID",
|
CertOID: "OID",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prefix for IPv4 encoded as IPv6 address
|
||||||
|
const ipv4InIPv6Prefix = "::ffff:"
|
||||||
|
|
||||||
//go:generate go run types_generate.go
|
//go:generate go run types_generate.go
|
||||||
|
|
||||||
// Question holds a DNS question. Usually there is just one. While the
|
// Question holds a DNS question. Usually there is just one. While the
|
||||||
|
@ -751,6 +754,11 @@ func (rr *AAAA) String() string {
|
||||||
if rr.AAAA == nil {
|
if rr.AAAA == nil {
|
||||||
return rr.Hdr.String()
|
return rr.Hdr.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rr.AAAA.To4() != nil {
|
||||||
|
return rr.Hdr.String() + ipv4InIPv6Prefix + rr.AAAA.String()
|
||||||
|
}
|
||||||
|
|
||||||
return rr.Hdr.String() + rr.AAAA.String()
|
return rr.Hdr.String() + rr.AAAA.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1517,7 +1525,7 @@ func (a *APLPrefix) str() string {
|
||||||
case net.IPv6len:
|
case net.IPv6len:
|
||||||
// add prefix for IPv4-mapped IPv6
|
// add prefix for IPv4-mapped IPv6
|
||||||
if v4 := a.Network.IP.To4(); v4 != nil {
|
if v4 := a.Network.IP.To4(); v4 != nil {
|
||||||
sb.WriteString("::ffff:")
|
sb.WriteString(ipv4InIPv6Prefix)
|
||||||
}
|
}
|
||||||
sb.WriteString(a.Network.IP.String())
|
sb.WriteString(a.Network.IP.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package dns
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// Version is current version of this library.
|
// Version is current version of this library.
|
||||||
var Version = v{1, 1, 55}
|
var Version = v{1, 1, 56}
|
||||||
|
|
||||||
// v holds the version of this library.
|
// v holds the version of this library.
|
||||||
type v struct {
|
type v struct {
|
||||||
|
|
|
@ -140,7 +140,7 @@ func Compare(v, w string) int {
|
||||||
// Max canonicalizes its arguments and then returns the version string
|
// Max canonicalizes its arguments and then returns the version string
|
||||||
// that compares greater.
|
// that compares greater.
|
||||||
//
|
//
|
||||||
// Deprecated: use Compare instead. In most cases, returning a canonicalized
|
// Deprecated: use [Compare] instead. In most cases, returning a canonicalized
|
||||||
// version is not expected or desired.
|
// version is not expected or desired.
|
||||||
func Max(v, w string) string {
|
func Max(v, w string) string {
|
||||||
v = Canonical(v)
|
v = Canonical(v)
|
||||||
|
@ -151,7 +151,7 @@ func Max(v, w string) string {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByVersion implements sort.Interface for sorting semantic version strings.
|
// ByVersion implements [sort.Interface] for sorting semantic version strings.
|
||||||
type ByVersion []string
|
type ByVersion []string
|
||||||
|
|
||||||
func (vs ByVersion) Len() int { return len(vs) }
|
func (vs ByVersion) Len() int { return len(vs) }
|
||||||
|
@ -164,7 +164,7 @@ func (vs ByVersion) Less(i, j int) bool {
|
||||||
return vs[i] < vs[j]
|
return vs[i] < vs[j]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort sorts a list of semantic version strings using ByVersion.
|
// Sort sorts a list of semantic version strings using [ByVersion].
|
||||||
func Sort(list []string) {
|
func Sort(list []string) {
|
||||||
sort.Sort(ByVersion(list))
|
sort.Sort(ByVersion(list))
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,15 +128,14 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package,
|
||||||
// (from "version"). Select appropriate importer.
|
// (from "version"). Select appropriate importer.
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
switch data[0] {
|
switch data[0] {
|
||||||
case 'i':
|
case 'v', 'c', 'd': // binary, till go1.10
|
||||||
|
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
|
||||||
|
|
||||||
|
case 'i': // indexed, till go1.19
|
||||||
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
|
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
|
||||||
return pkg, err
|
return pkg, err
|
||||||
|
|
||||||
case 'v', 'c', 'd':
|
case 'u': // unified, from go1.20
|
||||||
_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
|
|
||||||
return pkg, err
|
|
||||||
|
|
||||||
case 'u':
|
|
||||||
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
|
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
|
||||||
return pkg, err
|
return pkg, err
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ package packagesdriver
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/types"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/tools/internal/gocommand"
|
"golang.org/x/tools/internal/gocommand"
|
||||||
|
@ -16,7 +15,7 @@ import (
|
||||||
|
|
||||||
var debug = false
|
var debug = false
|
||||||
|
|
||||||
func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (types.Sizes, error) {
|
func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) {
|
||||||
inv.Verb = "list"
|
inv.Verb = "list"
|
||||||
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}
|
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}
|
||||||
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
|
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
|
||||||
|
@ -29,21 +28,21 @@ func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *
|
||||||
inv.Args = []string{"GOARCH"}
|
inv.Args = []string{"GOARCH"}
|
||||||
envout, enverr := gocmdRunner.Run(ctx, inv)
|
envout, enverr := gocmdRunner.Run(ctx, inv)
|
||||||
if enverr != nil {
|
if enverr != nil {
|
||||||
return nil, enverr
|
return "", "", enverr
|
||||||
}
|
}
|
||||||
goarch = strings.TrimSpace(envout.String())
|
goarch = strings.TrimSpace(envout.String())
|
||||||
compiler = "gc"
|
compiler = "gc"
|
||||||
} else {
|
} else {
|
||||||
return nil, friendlyErr
|
return "", "", friendlyErr
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fields := strings.Fields(stdout.String())
|
fields := strings.Fields(stdout.String())
|
||||||
if len(fields) < 2 {
|
if len(fields) < 2 {
|
||||||
return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
|
return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
|
||||||
stdout.String(), stderr.String())
|
stdout.String(), stderr.String())
|
||||||
}
|
}
|
||||||
goarch = fields[0]
|
goarch = fields[0]
|
||||||
compiler = fields[1]
|
compiler = fields[1]
|
||||||
}
|
}
|
||||||
return types.SizesFor(compiler, goarch), nil
|
return compiler, goarch, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/types"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -153,10 +152,10 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
||||||
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
|
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
|
||||||
sizeswg.Add(1)
|
sizeswg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
var sizes types.Sizes
|
compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
|
||||||
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
|
sizeserr = err
|
||||||
// types.SizesFor always returns nil or a *types.StdSizes.
|
response.dr.Compiler = compiler
|
||||||
response.dr.Sizes, _ = sizes.(*types.StdSizes)
|
response.dr.Arch = arch
|
||||||
sizeswg.Done()
|
sizeswg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -625,7 +624,12 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
if pkg.PkgPath == "unsafe" {
|
if pkg.PkgPath == "unsafe" {
|
||||||
pkg.GoFiles = nil // ignore fake unsafe.go file
|
pkg.CompiledGoFiles = nil // ignore fake unsafe.go file (#59929)
|
||||||
|
} else if len(pkg.CompiledGoFiles) == 0 {
|
||||||
|
// Work around for pre-go.1.11 versions of go list.
|
||||||
|
// TODO(matloob): they should be handled by the fallback.
|
||||||
|
// Can we delete this?
|
||||||
|
pkg.CompiledGoFiles = pkg.GoFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume go list emits only absolute paths for Dir.
|
// Assume go list emits only absolute paths for Dir.
|
||||||
|
@ -663,16 +667,12 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
||||||
response.Roots = append(response.Roots, pkg.ID)
|
response.Roots = append(response.Roots, pkg.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Work around for pre-go.1.11 versions of go list.
|
|
||||||
// TODO(matloob): they should be handled by the fallback.
|
|
||||||
// Can we delete this?
|
|
||||||
if len(pkg.CompiledGoFiles) == 0 {
|
|
||||||
pkg.CompiledGoFiles = pkg.GoFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporary work-around for golang/go#39986. Parse filenames out of
|
// Temporary work-around for golang/go#39986. Parse filenames out of
|
||||||
// error messages. This happens if there are unrecoverable syntax
|
// error messages. This happens if there are unrecoverable syntax
|
||||||
// errors in the source, so we can't match on a specific error message.
|
// errors in the source, so we can't match on a specific error message.
|
||||||
|
//
|
||||||
|
// TODO(rfindley): remove this heuristic, in favor of considering
|
||||||
|
// InvalidGoFiles from the list driver.
|
||||||
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
|
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
|
||||||
addFilenameFromPos := func(pos string) bool {
|
addFilenameFromPos := func(pos string) bool {
|
||||||
split := strings.Split(pos, ":")
|
split := strings.Split(pos, ":")
|
||||||
|
@ -891,6 +891,15 @@ func golistargs(cfg *Config, words []string, goVersion int) []string {
|
||||||
// probably because you'd just get the TestMain.
|
// probably because you'd just get the TestMain.
|
||||||
fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)),
|
fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// golang/go#60456: with go1.21 and later, go list serves pgo variants, which
|
||||||
|
// can be costly to compute and may result in redundant processing for the
|
||||||
|
// caller. Disable these variants. If someone wants to add e.g. a NeedPGO
|
||||||
|
// mode flag, that should be a separate proposal.
|
||||||
|
if goVersion >= 21 {
|
||||||
|
fullargs = append(fullargs, "-pgo=off")
|
||||||
|
}
|
||||||
|
|
||||||
fullargs = append(fullargs, cfg.BuildFlags...)
|
fullargs = append(fullargs, cfg.BuildFlags...)
|
||||||
fullargs = append(fullargs, "--")
|
fullargs = append(fullargs, "--")
|
||||||
fullargs = append(fullargs, words...)
|
fullargs = append(fullargs, words...)
|
||||||
|
|
|
@ -220,8 +220,10 @@ type driverResponse struct {
|
||||||
// lists of multiple drivers, go/packages will fall back to the next driver.
|
// lists of multiple drivers, go/packages will fall back to the next driver.
|
||||||
NotHandled bool
|
NotHandled bool
|
||||||
|
|
||||||
// Sizes, if not nil, is the types.Sizes to use when type checking.
|
// Compiler and Arch are the arguments pass of types.SizesFor
|
||||||
Sizes *types.StdSizes
|
// to get a types.Sizes to use when type checking.
|
||||||
|
Compiler string
|
||||||
|
Arch string
|
||||||
|
|
||||||
// Roots is the set of package IDs that make up the root packages.
|
// Roots is the set of package IDs that make up the root packages.
|
||||||
// We have to encode this separately because when we encode a single package
|
// We have to encode this separately because when we encode a single package
|
||||||
|
@ -262,7 +264,7 @@ func Load(cfg *Config, patterns ...string) ([]*Package, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
l.sizes = response.Sizes
|
l.sizes = types.SizesFor(response.Compiler, response.Arch)
|
||||||
return l.refine(response)
|
return l.refine(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +310,9 @@ type Package struct {
|
||||||
TypeErrors []types.Error
|
TypeErrors []types.Error
|
||||||
|
|
||||||
// GoFiles lists the absolute file paths of the package's Go source files.
|
// GoFiles lists the absolute file paths of the package's Go source files.
|
||||||
|
// It may include files that should not be compiled, for example because
|
||||||
|
// they contain non-matching build tags, are documentary pseudo-files such as
|
||||||
|
// unsafe/unsafe.go or builtin/builtin.go, or are subject to cgo preprocessing.
|
||||||
GoFiles []string
|
GoFiles []string
|
||||||
|
|
||||||
// CompiledGoFiles lists the absolute file paths of the package's source
|
// CompiledGoFiles lists the absolute file paths of the package's source
|
||||||
|
@ -627,7 +632,7 @@ func newLoader(cfg *Config) *loader {
|
||||||
return ld
|
return ld
|
||||||
}
|
}
|
||||||
|
|
||||||
// refine connects the supplied packages into a graph and then adds type and
|
// refine connects the supplied packages into a graph and then adds type
|
||||||
// and syntax information as requested by the LoadMode.
|
// and syntax information as requested by the LoadMode.
|
||||||
func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
|
func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
|
||||||
roots := response.Roots
|
roots := response.Roots
|
||||||
|
@ -1040,6 +1045,9 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||||
Error: appendError,
|
Error: appendError,
|
||||||
Sizes: ld.sizes,
|
Sizes: ld.sizes,
|
||||||
}
|
}
|
||||||
|
if lpkg.Module != nil && lpkg.Module.GoVersion != "" {
|
||||||
|
typesinternal.SetGoVersion(tc, "go"+lpkg.Module.GoVersion)
|
||||||
|
}
|
||||||
if (ld.Mode & typecheckCgo) != 0 {
|
if (ld.Mode & typecheckCgo) != 0 {
|
||||||
if !typesinternal.SetUsesCgo(tc) {
|
if !typesinternal.SetUsesCgo(tc) {
|
||||||
appendError(Error{
|
appendError(Error{
|
||||||
|
|
|
@ -0,0 +1,827 @@
|
||||||
|
// Copyright 2018 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 objectpath defines a naming scheme for types.Objects
|
||||||
|
// (that is, named entities in Go programs) relative to their enclosing
|
||||||
|
// package.
|
||||||
|
//
|
||||||
|
// Type-checker objects are canonical, so they are usually identified by
|
||||||
|
// their address in memory (a pointer), but a pointer has meaning only
|
||||||
|
// within one address space. By contrast, objectpath names allow the
|
||||||
|
// identity of an object to be sent from one program to another,
|
||||||
|
// establishing a correspondence between types.Object variables that are
|
||||||
|
// distinct but logically equivalent.
|
||||||
|
//
|
||||||
|
// A single object may have multiple paths. In this example,
|
||||||
|
//
|
||||||
|
// type A struct{ X int }
|
||||||
|
// type B A
|
||||||
|
//
|
||||||
|
// the field X has two paths due to its membership of both A and B.
|
||||||
|
// The For(obj) function always returns one of these paths, arbitrarily
|
||||||
|
// but consistently.
|
||||||
|
package objectpath
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/types"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/typeparams"
|
||||||
|
"golang.org/x/tools/internal/typesinternal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Path is an opaque name that identifies a types.Object
|
||||||
|
// relative to its package. Conceptually, the name consists of a
|
||||||
|
// sequence of destructuring operations applied to the package scope
|
||||||
|
// to obtain the original object.
|
||||||
|
// The name does not include the package itself.
|
||||||
|
type Path string
|
||||||
|
|
||||||
|
// Encoding
|
||||||
|
//
|
||||||
|
// An object path is a textual and (with training) human-readable encoding
|
||||||
|
// of a sequence of destructuring operators, starting from a types.Package.
|
||||||
|
// The sequences represent a path through the package/object/type graph.
|
||||||
|
// We classify these operators by their type:
|
||||||
|
//
|
||||||
|
// PO package->object Package.Scope.Lookup
|
||||||
|
// OT object->type Object.Type
|
||||||
|
// TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU]
|
||||||
|
// TO type->object Type.{At,Field,Method,Obj} [AFMO]
|
||||||
|
//
|
||||||
|
// All valid paths start with a package and end at an object
|
||||||
|
// and thus may be defined by the regular language:
|
||||||
|
//
|
||||||
|
// objectpath = PO (OT TT* TO)*
|
||||||
|
//
|
||||||
|
// The concrete encoding follows directly:
|
||||||
|
// - The only PO operator is Package.Scope.Lookup, which requires an identifier.
|
||||||
|
// - The only OT operator is Object.Type,
|
||||||
|
// which we encode as '.' because dot cannot appear in an identifier.
|
||||||
|
// - The TT operators are encoded as [EKPRUTC];
|
||||||
|
// one of these (TypeParam) requires an integer operand,
|
||||||
|
// which is encoded as a string of decimal digits.
|
||||||
|
// - The TO operators are encoded as [AFMO];
|
||||||
|
// three of these (At,Field,Method) require an integer operand,
|
||||||
|
// which is encoded as a string of decimal digits.
|
||||||
|
// These indices are stable across different representations
|
||||||
|
// of the same package, even source and export data.
|
||||||
|
// The indices used are implementation specific and may not correspond to
|
||||||
|
// the argument to the go/types function.
|
||||||
|
//
|
||||||
|
// In the example below,
|
||||||
|
//
|
||||||
|
// package p
|
||||||
|
//
|
||||||
|
// type T interface {
|
||||||
|
// f() (a string, b struct{ X int })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// field X has the path "T.UM0.RA1.F0",
|
||||||
|
// representing the following sequence of operations:
|
||||||
|
//
|
||||||
|
// p.Lookup("T") T
|
||||||
|
// .Type().Underlying().Method(0). f
|
||||||
|
// .Type().Results().At(1) b
|
||||||
|
// .Type().Field(0) X
|
||||||
|
//
|
||||||
|
// The encoding is not maximally compact---every R or P is
|
||||||
|
// followed by an A, for example---but this simplifies the
|
||||||
|
// encoder and decoder.
|
||||||
|
const (
|
||||||
|
// object->type operators
|
||||||
|
opType = '.' // .Type() (Object)
|
||||||
|
|
||||||
|
// type->type operators
|
||||||
|
opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map)
|
||||||
|
opKey = 'K' // .Key() (Map)
|
||||||
|
opParams = 'P' // .Params() (Signature)
|
||||||
|
opResults = 'R' // .Results() (Signature)
|
||||||
|
opUnderlying = 'U' // .Underlying() (Named)
|
||||||
|
opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature)
|
||||||
|
opConstraint = 'C' // .Constraint() (TypeParam)
|
||||||
|
|
||||||
|
// type->object operators
|
||||||
|
opAt = 'A' // .At(i) (Tuple)
|
||||||
|
opField = 'F' // .Field(i) (Struct)
|
||||||
|
opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
|
||||||
|
opObj = 'O' // .Obj() (Named, TypeParam)
|
||||||
|
)
|
||||||
|
|
||||||
|
// For is equivalent to new(Encoder).For(obj).
|
||||||
|
//
|
||||||
|
// It may be more efficient to reuse a single Encoder across several calls.
|
||||||
|
func For(obj types.Object) (Path, error) {
|
||||||
|
return new(Encoder).For(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Encoder amortizes the cost of encoding the paths of multiple objects.
|
||||||
|
// The zero value of an Encoder is ready to use.
|
||||||
|
type Encoder struct {
|
||||||
|
scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects
|
||||||
|
namedMethodsMemo map[*types.Named][]*types.Func // memoization of namedMethods()
|
||||||
|
skipMethodSorting bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose back doors so that gopls can avoid method sorting, which can dominate
|
||||||
|
// analysis on certain repositories.
|
||||||
|
//
|
||||||
|
// TODO(golang/go#61443): remove this.
|
||||||
|
func init() {
|
||||||
|
typesinternal.SkipEncoderMethodSorting = func(enc interface{}) {
|
||||||
|
enc.(*Encoder).skipMethodSorting = true
|
||||||
|
}
|
||||||
|
typesinternal.ObjectpathObject = object
|
||||||
|
}
|
||||||
|
|
||||||
|
// For returns the path to an object relative to its package,
|
||||||
|
// or an error if the object is not accessible from the package's Scope.
|
||||||
|
//
|
||||||
|
// The For function guarantees to return a path only for the following objects:
|
||||||
|
// - package-level types
|
||||||
|
// - exported package-level non-types
|
||||||
|
// - methods
|
||||||
|
// - parameter and result variables
|
||||||
|
// - struct fields
|
||||||
|
// These objects are sufficient to define the API of their package.
|
||||||
|
// The objects described by a package's export data are drawn from this set.
|
||||||
|
//
|
||||||
|
// The set of objects accessible from a package's Scope depends on
|
||||||
|
// whether the package was produced by type-checking syntax, or
|
||||||
|
// reading export data; the latter may have a smaller Scope since
|
||||||
|
// export data trims objects that are not reachable from an exported
|
||||||
|
// declaration. For example, the For function will return a path for
|
||||||
|
// an exported method of an unexported type that is not reachable
|
||||||
|
// from any public declaration; this path will cause the Object
|
||||||
|
// function to fail if called on a package loaded from export data.
|
||||||
|
// TODO(adonovan): is this a bug or feature? Should this package
|
||||||
|
// compute accessibility in the same way?
|
||||||
|
//
|
||||||
|
// For does not return a path for predeclared names, imported package
|
||||||
|
// names, local names, and unexported package-level names (except
|
||||||
|
// types).
|
||||||
|
//
|
||||||
|
// Example: given this definition,
|
||||||
|
//
|
||||||
|
// package p
|
||||||
|
//
|
||||||
|
// type T interface {
|
||||||
|
// f() (a string, b struct{ X int })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// For(X) would return a path that denotes the following sequence of operations:
|
||||||
|
//
|
||||||
|
// p.Scope().Lookup("T") (TypeName T)
|
||||||
|
// .Type().Underlying().Method(0). (method Func f)
|
||||||
|
// .Type().Results().At(1) (field Var b)
|
||||||
|
// .Type().Field(0) (field Var X)
|
||||||
|
//
|
||||||
|
// where p is the package (*types.Package) to which X belongs.
|
||||||
|
func (enc *Encoder) For(obj types.Object) (Path, error) {
|
||||||
|
pkg := obj.Pkg()
|
||||||
|
|
||||||
|
// This table lists the cases of interest.
|
||||||
|
//
|
||||||
|
// Object Action
|
||||||
|
// ------ ------
|
||||||
|
// nil reject
|
||||||
|
// builtin reject
|
||||||
|
// pkgname reject
|
||||||
|
// label reject
|
||||||
|
// var
|
||||||
|
// package-level accept
|
||||||
|
// func param/result accept
|
||||||
|
// local reject
|
||||||
|
// struct field accept
|
||||||
|
// const
|
||||||
|
// package-level accept
|
||||||
|
// local reject
|
||||||
|
// func
|
||||||
|
// package-level accept
|
||||||
|
// init functions reject
|
||||||
|
// concrete method accept
|
||||||
|
// interface method accept
|
||||||
|
// type
|
||||||
|
// package-level accept
|
||||||
|
// local reject
|
||||||
|
//
|
||||||
|
// The only accessible package-level objects are members of pkg itself.
|
||||||
|
//
|
||||||
|
// The cases are handled in four steps:
|
||||||
|
//
|
||||||
|
// 1. reject nil and builtin
|
||||||
|
// 2. accept package-level objects
|
||||||
|
// 3. reject obviously invalid objects
|
||||||
|
// 4. search the API for the path to the param/result/field/method.
|
||||||
|
|
||||||
|
// 1. reference to nil or builtin?
|
||||||
|
if pkg == nil {
|
||||||
|
return "", fmt.Errorf("predeclared %s has no path", obj)
|
||||||
|
}
|
||||||
|
scope := pkg.Scope()
|
||||||
|
|
||||||
|
// 2. package-level object?
|
||||||
|
if scope.Lookup(obj.Name()) == obj {
|
||||||
|
// Only exported objects (and non-exported types) have a path.
|
||||||
|
// Non-exported types may be referenced by other objects.
|
||||||
|
if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
|
||||||
|
return "", fmt.Errorf("no path for non-exported %v", obj)
|
||||||
|
}
|
||||||
|
return Path(obj.Name()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Not a package-level object.
|
||||||
|
// Reject obviously non-viable cases.
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *types.TypeName:
|
||||||
|
if _, ok := obj.Type().(*typeparams.TypeParam); !ok {
|
||||||
|
// With the exception of type parameters, only package-level type names
|
||||||
|
// have a path.
|
||||||
|
return "", fmt.Errorf("no path for %v", obj)
|
||||||
|
}
|
||||||
|
case *types.Const, // Only package-level constants have a path.
|
||||||
|
*types.Label, // Labels are function-local.
|
||||||
|
*types.PkgName: // PkgNames are file-local.
|
||||||
|
return "", fmt.Errorf("no path for %v", obj)
|
||||||
|
|
||||||
|
case *types.Var:
|
||||||
|
// Could be:
|
||||||
|
// - a field (obj.IsField())
|
||||||
|
// - a func parameter or result
|
||||||
|
// - a local var.
|
||||||
|
// Sadly there is no way to distinguish
|
||||||
|
// a param/result from a local
|
||||||
|
// so we must proceed to the find.
|
||||||
|
|
||||||
|
case *types.Func:
|
||||||
|
// A func, if not package-level, must be a method.
|
||||||
|
if recv := obj.Type().(*types.Signature).Recv(); recv == nil {
|
||||||
|
return "", fmt.Errorf("func is not a method: %v", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
if path, ok := enc.concreteMethod(obj); ok {
|
||||||
|
// Fast path for concrete methods that avoids looping over scope.
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Search the API for the path to the var (field/param/result) or method.
|
||||||
|
|
||||||
|
// First inspect package-level named types.
|
||||||
|
// In the presence of path aliases, these give
|
||||||
|
// the best paths because non-types may
|
||||||
|
// refer to types, but not the reverse.
|
||||||
|
empty := make([]byte, 0, 48) // initial space
|
||||||
|
objs := enc.scopeObjects(scope)
|
||||||
|
for _, o := range objs {
|
||||||
|
tname, ok := o.(*types.TypeName)
|
||||||
|
if !ok {
|
||||||
|
continue // handle non-types in second pass
|
||||||
|
}
|
||||||
|
|
||||||
|
path := append(empty, o.Name()...)
|
||||||
|
path = append(path, opType)
|
||||||
|
|
||||||
|
T := o.Type()
|
||||||
|
|
||||||
|
if tname.IsAlias() {
|
||||||
|
// type alias
|
||||||
|
if r := find(obj, T, path, nil); r != nil {
|
||||||
|
return Path(r), nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if named, _ := T.(*types.Named); named != nil {
|
||||||
|
if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil {
|
||||||
|
// generic named type
|
||||||
|
return Path(r), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// defined (named) type
|
||||||
|
if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil {
|
||||||
|
return Path(r), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then inspect everything else:
|
||||||
|
// non-types, and declared methods of defined types.
|
||||||
|
for _, o := range objs {
|
||||||
|
path := append(empty, o.Name()...)
|
||||||
|
if _, ok := o.(*types.TypeName); !ok {
|
||||||
|
if o.Exported() {
|
||||||
|
// exported non-type (const, var, func)
|
||||||
|
if r := find(obj, o.Type(), append(path, opType), nil); r != nil {
|
||||||
|
return Path(r), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect declared methods of defined types.
|
||||||
|
if T, ok := o.Type().(*types.Named); ok {
|
||||||
|
path = append(path, opType)
|
||||||
|
if !enc.skipMethodSorting {
|
||||||
|
// Note that method index here is always with respect
|
||||||
|
// to canonical ordering of methods, regardless of how
|
||||||
|
// they appear in the underlying type.
|
||||||
|
for i, m := range enc.namedMethods(T) {
|
||||||
|
path2 := appendOpArg(path, opMethod, i)
|
||||||
|
if m == obj {
|
||||||
|
return Path(path2), nil // found declared method
|
||||||
|
}
|
||||||
|
if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
|
||||||
|
return Path(r), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This branch must match the logic in the branch above, using go/types
|
||||||
|
// APIs without sorting.
|
||||||
|
for i := 0; i < T.NumMethods(); i++ {
|
||||||
|
m := T.Method(i)
|
||||||
|
path2 := appendOpArg(path, opMethod, i)
|
||||||
|
if m == obj {
|
||||||
|
return Path(path2), nil // found declared method
|
||||||
|
}
|
||||||
|
if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
|
||||||
|
return Path(r), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendOpArg(path []byte, op byte, arg int) []byte {
|
||||||
|
path = append(path, op)
|
||||||
|
path = strconv.AppendInt(path, int64(arg), 10)
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// concreteMethod returns the path for meth, which must have a non-nil receiver.
|
||||||
|
// The second return value indicates success and may be false if the method is
|
||||||
|
// an interface method or if it is an instantiated method.
|
||||||
|
//
|
||||||
|
// This function is just an optimization that avoids the general scope walking
|
||||||
|
// approach. You are expected to fall back to the general approach if this
|
||||||
|
// function fails.
|
||||||
|
func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
|
||||||
|
// Concrete methods can only be declared on package-scoped named types. For
|
||||||
|
// that reason we can skip the expensive walk over the package scope: the
|
||||||
|
// path will always be package -> named type -> method. We can trivially get
|
||||||
|
// the type name from the receiver, and only have to look over the type's
|
||||||
|
// methods to find the method index.
|
||||||
|
//
|
||||||
|
// Methods on generic types require special consideration, however. Consider
|
||||||
|
// the following package:
|
||||||
|
//
|
||||||
|
// L1: type S[T any] struct{}
|
||||||
|
// L2: func (recv S[A]) Foo() { recv.Bar() }
|
||||||
|
// L3: func (recv S[B]) Bar() { }
|
||||||
|
// L4: type Alias = S[int]
|
||||||
|
// L5: func _[T any]() { var s S[int]; s.Foo() }
|
||||||
|
//
|
||||||
|
// The receivers of methods on generic types are instantiations. L2 and L3
|
||||||
|
// instantiate S with the type-parameters A and B, which are scoped to the
|
||||||
|
// respective methods. L4 and L5 each instantiate S with int. Each of these
|
||||||
|
// instantiations has its own method set, full of methods (and thus objects)
|
||||||
|
// with receivers whose types are the respective instantiations. In other
|
||||||
|
// words, we have
|
||||||
|
//
|
||||||
|
// S[A].Foo, S[A].Bar
|
||||||
|
// S[B].Foo, S[B].Bar
|
||||||
|
// S[int].Foo, S[int].Bar
|
||||||
|
//
|
||||||
|
// We may thus be trying to produce object paths for any of these objects.
|
||||||
|
//
|
||||||
|
// S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo
|
||||||
|
// and S.Bar, which are the paths that this function naturally produces.
|
||||||
|
//
|
||||||
|
// S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that
|
||||||
|
// don't correspond to the origin methods. For S[int], this is significant.
|
||||||
|
// The most precise object path for S[int].Foo, for example, is Alias.Foo,
|
||||||
|
// not S.Foo. Our function, however, would produce S.Foo, which would
|
||||||
|
// resolve to a different object.
|
||||||
|
//
|
||||||
|
// For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are
|
||||||
|
// still the correct paths, since only the origin methods have meaningful
|
||||||
|
// paths. But this is likely only true for trivial cases and has edge cases.
|
||||||
|
// Since this function is only an optimization, we err on the side of giving
|
||||||
|
// up, deferring to the slower but definitely correct algorithm. Most users
|
||||||
|
// of objectpath will only be giving us origin methods, anyway, as referring
|
||||||
|
// to instantiated methods is usually not useful.
|
||||||
|
|
||||||
|
if typeparams.OriginMethod(meth) != meth {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
recvT := meth.Type().(*types.Signature).Recv().Type()
|
||||||
|
if ptr, ok := recvT.(*types.Pointer); ok {
|
||||||
|
recvT = ptr.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
named, ok := recvT.(*types.Named)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
if types.IsInterface(named) {
|
||||||
|
// Named interfaces don't have to be package-scoped
|
||||||
|
//
|
||||||
|
// TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface
|
||||||
|
// methods, too, I think.
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preallocate space for the name, opType, opMethod, and some digits.
|
||||||
|
name := named.Obj().Name()
|
||||||
|
path := make([]byte, 0, len(name)+8)
|
||||||
|
path = append(path, name...)
|
||||||
|
path = append(path, opType)
|
||||||
|
|
||||||
|
if !enc.skipMethodSorting {
|
||||||
|
for i, m := range enc.namedMethods(named) {
|
||||||
|
if m == meth {
|
||||||
|
path = appendOpArg(path, opMethod, i)
|
||||||
|
return Path(path), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This branch must match the logic of the branch above, using go/types
|
||||||
|
// APIs without sorting.
|
||||||
|
for i := 0; i < named.NumMethods(); i++ {
|
||||||
|
m := named.Method(i)
|
||||||
|
if m == meth {
|
||||||
|
path = appendOpArg(path, opMethod, i)
|
||||||
|
return Path(path), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due to golang/go#59944, go/types fails to associate the receiver with
|
||||||
|
// certain methods on cgo types.
|
||||||
|
//
|
||||||
|
// TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go
|
||||||
|
// versions gopls supports.
|
||||||
|
return "", false
|
||||||
|
// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// find finds obj within type T, returning the path to it, or nil if not found.
|
||||||
|
//
|
||||||
|
// The seen map is used to short circuit cycles through type parameters. If
|
||||||
|
// nil, it will be allocated as necessary.
|
||||||
|
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
|
||||||
|
switch T := T.(type) {
|
||||||
|
case *types.Basic, *types.Named:
|
||||||
|
// Named types belonging to pkg were handled already,
|
||||||
|
// so T must belong to another package. No path.
|
||||||
|
return nil
|
||||||
|
case *types.Pointer:
|
||||||
|
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||||
|
case *types.Slice:
|
||||||
|
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||||
|
case *types.Array:
|
||||||
|
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||||
|
case *types.Chan:
|
||||||
|
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||||
|
case *types.Map:
|
||||||
|
if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||||
|
case *types.Signature:
|
||||||
|
if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return find(obj, T.Results(), append(path, opResults), seen)
|
||||||
|
case *types.Struct:
|
||||||
|
for i := 0; i < T.NumFields(); i++ {
|
||||||
|
fld := T.Field(i)
|
||||||
|
path2 := appendOpArg(path, opField, i)
|
||||||
|
if fld == obj {
|
||||||
|
return path2 // found field var
|
||||||
|
}
|
||||||
|
if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case *types.Tuple:
|
||||||
|
for i := 0; i < T.Len(); i++ {
|
||||||
|
v := T.At(i)
|
||||||
|
path2 := appendOpArg(path, opAt, i)
|
||||||
|
if v == obj {
|
||||||
|
return path2 // found param/result var
|
||||||
|
}
|
||||||
|
if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case *types.Interface:
|
||||||
|
for i := 0; i < T.NumMethods(); i++ {
|
||||||
|
m := T.Method(i)
|
||||||
|
path2 := appendOpArg(path, opMethod, i)
|
||||||
|
if m == obj {
|
||||||
|
return path2 // found interface method
|
||||||
|
}
|
||||||
|
if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case *typeparams.TypeParam:
|
||||||
|
name := T.Obj()
|
||||||
|
if name == obj {
|
||||||
|
return append(path, opObj)
|
||||||
|
}
|
||||||
|
if seen[name] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if seen == nil {
|
||||||
|
seen = make(map[*types.TypeName]bool)
|
||||||
|
}
|
||||||
|
seen[name] = true
|
||||||
|
if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
panic(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte {
|
||||||
|
for i := 0; i < list.Len(); i++ {
|
||||||
|
tparam := list.At(i)
|
||||||
|
path2 := appendOpArg(path, opTypeParam, i)
|
||||||
|
if r := find(obj, tparam, path2, seen); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object returns the object denoted by path p within the package pkg.
|
||||||
|
func Object(pkg *types.Package, p Path) (types.Object, error) {
|
||||||
|
return object(pkg, string(p), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: the skipMethodSorting parameter must match the value of
|
||||||
|
// Encoder.skipMethodSorting used during encoding.
|
||||||
|
func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.Object, error) {
|
||||||
|
if pathstr == "" {
|
||||||
|
return nil, fmt.Errorf("empty path")
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkgobj, suffix string
|
||||||
|
if dot := strings.IndexByte(pathstr, opType); dot < 0 {
|
||||||
|
pkgobj = pathstr
|
||||||
|
} else {
|
||||||
|
pkgobj = pathstr[:dot]
|
||||||
|
suffix = pathstr[dot:] // suffix starts with "."
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := pkg.Scope().Lookup(pkgobj)
|
||||||
|
if obj == nil {
|
||||||
|
return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// abstraction of *types.{Pointer,Slice,Array,Chan,Map}
|
||||||
|
type hasElem interface {
|
||||||
|
Elem() types.Type
|
||||||
|
}
|
||||||
|
// abstraction of *types.{Named,Signature}
|
||||||
|
type hasTypeParams interface {
|
||||||
|
TypeParams() *typeparams.TypeParamList
|
||||||
|
}
|
||||||
|
// abstraction of *types.{Named,TypeParam}
|
||||||
|
type hasObj interface {
|
||||||
|
Obj() *types.TypeName
|
||||||
|
}
|
||||||
|
|
||||||
|
// The loop state is the pair (t, obj),
|
||||||
|
// exactly one of which is non-nil, initially obj.
|
||||||
|
// All suffixes start with '.' (the only object->type operation),
|
||||||
|
// followed by optional type->type operations,
|
||||||
|
// then a type->object operation.
|
||||||
|
// The cycle then repeats.
|
||||||
|
var t types.Type
|
||||||
|
for suffix != "" {
|
||||||
|
code := suffix[0]
|
||||||
|
suffix = suffix[1:]
|
||||||
|
|
||||||
|
// Codes [AFM] have an integer operand.
|
||||||
|
var index int
|
||||||
|
switch code {
|
||||||
|
case opAt, opField, opMethod, opTypeParam:
|
||||||
|
rest := strings.TrimLeft(suffix, "0123456789")
|
||||||
|
numerals := suffix[:len(suffix)-len(rest)]
|
||||||
|
suffix = rest
|
||||||
|
i, err := strconv.Atoi(numerals)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code)
|
||||||
|
}
|
||||||
|
index = int(i)
|
||||||
|
case opObj:
|
||||||
|
// no operand
|
||||||
|
default:
|
||||||
|
// The suffix must end with a type->object operation.
|
||||||
|
if suffix == "" {
|
||||||
|
return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if code == opType {
|
||||||
|
if t != nil {
|
||||||
|
return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType)
|
||||||
|
}
|
||||||
|
t = obj.Type()
|
||||||
|
obj = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if t == nil {
|
||||||
|
return nil, fmt.Errorf("invalid path: code %q in object context", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inv: t != nil, obj == nil
|
||||||
|
|
||||||
|
switch code {
|
||||||
|
case opElem:
|
||||||
|
hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t)
|
||||||
|
}
|
||||||
|
t = hasElem.Elem()
|
||||||
|
|
||||||
|
case opKey:
|
||||||
|
mapType, ok := t.(*types.Map)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t)
|
||||||
|
}
|
||||||
|
t = mapType.Key()
|
||||||
|
|
||||||
|
case opParams:
|
||||||
|
sig, ok := t.(*types.Signature)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
|
||||||
|
}
|
||||||
|
t = sig.Params()
|
||||||
|
|
||||||
|
case opResults:
|
||||||
|
sig, ok := t.(*types.Signature)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
|
||||||
|
}
|
||||||
|
t = sig.Results()
|
||||||
|
|
||||||
|
case opUnderlying:
|
||||||
|
named, ok := t.(*types.Named)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t)
|
||||||
|
}
|
||||||
|
t = named.Underlying()
|
||||||
|
|
||||||
|
case opTypeParam:
|
||||||
|
hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t)
|
||||||
|
}
|
||||||
|
tparams := hasTypeParams.TypeParams()
|
||||||
|
if n := tparams.Len(); index >= n {
|
||||||
|
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
||||||
|
}
|
||||||
|
t = tparams.At(index)
|
||||||
|
|
||||||
|
case opConstraint:
|
||||||
|
tparam, ok := t.(*typeparams.TypeParam)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t)
|
||||||
|
}
|
||||||
|
t = tparam.Constraint()
|
||||||
|
|
||||||
|
case opAt:
|
||||||
|
tuple, ok := t.(*types.Tuple)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t)
|
||||||
|
}
|
||||||
|
if n := tuple.Len(); index >= n {
|
||||||
|
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
||||||
|
}
|
||||||
|
obj = tuple.At(index)
|
||||||
|
t = nil
|
||||||
|
|
||||||
|
case opField:
|
||||||
|
structType, ok := t.(*types.Struct)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t)
|
||||||
|
}
|
||||||
|
if n := structType.NumFields(); index >= n {
|
||||||
|
return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n)
|
||||||
|
}
|
||||||
|
obj = structType.Field(index)
|
||||||
|
t = nil
|
||||||
|
|
||||||
|
case opMethod:
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *types.Interface:
|
||||||
|
if index >= t.NumMethods() {
|
||||||
|
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
|
||||||
|
}
|
||||||
|
obj = t.Method(index) // Id-ordered
|
||||||
|
|
||||||
|
case *types.Named:
|
||||||
|
if index >= t.NumMethods() {
|
||||||
|
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
|
||||||
|
}
|
||||||
|
if skipMethodSorting {
|
||||||
|
obj = t.Method(index)
|
||||||
|
} else {
|
||||||
|
methods := namedMethods(t) // (unmemoized)
|
||||||
|
obj = methods[index] // Id-ordered
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t)
|
||||||
|
}
|
||||||
|
t = nil
|
||||||
|
|
||||||
|
case opObj:
|
||||||
|
hasObj, ok := t.(hasObj)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t)
|
||||||
|
}
|
||||||
|
obj = hasObj.Obj()
|
||||||
|
t = nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid path: unknown code %q", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.Pkg() != pkg {
|
||||||
|
return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj, nil // success
|
||||||
|
}
|
||||||
|
|
||||||
|
// namedMethods returns the methods of a Named type in ascending Id order.
|
||||||
|
func namedMethods(named *types.Named) []*types.Func {
|
||||||
|
methods := make([]*types.Func, named.NumMethods())
|
||||||
|
for i := range methods {
|
||||||
|
methods[i] = named.Method(i)
|
||||||
|
}
|
||||||
|
sort.Slice(methods, func(i, j int) bool {
|
||||||
|
return methods[i].Id() < methods[j].Id()
|
||||||
|
})
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
|
// namedMethods is a memoization of the namedMethods function. Callers must not modify the result.
|
||||||
|
func (enc *Encoder) namedMethods(named *types.Named) []*types.Func {
|
||||||
|
m := enc.namedMethodsMemo
|
||||||
|
if m == nil {
|
||||||
|
m = make(map[*types.Named][]*types.Func)
|
||||||
|
enc.namedMethodsMemo = m
|
||||||
|
}
|
||||||
|
methods, ok := m[named]
|
||||||
|
if !ok {
|
||||||
|
methods = namedMethods(named) // allocates and sorts
|
||||||
|
m[named] = methods
|
||||||
|
}
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
|
// scopeObjects is a memoization of scope objects.
|
||||||
|
// Callers must not modify the result.
|
||||||
|
func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
|
||||||
|
m := enc.scopeMemo
|
||||||
|
if m == nil {
|
||||||
|
m = make(map[*types.Scope][]types.Object)
|
||||||
|
enc.scopeMemo = m
|
||||||
|
}
|
||||||
|
objs, ok := m[scope]
|
||||||
|
if !ok {
|
||||||
|
names := scope.Names() // allocates and sorts
|
||||||
|
objs = make([]types.Object, len(names))
|
||||||
|
for i, name := range names {
|
||||||
|
objs[i] = scope.Lookup(name)
|
||||||
|
}
|
||||||
|
m[scope] = objs
|
||||||
|
}
|
||||||
|
return objs
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2019 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 tag provides the labels used for telemetry throughout gopls.
|
||||||
|
package tag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/tools/internal/event/keys"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// create the label keys we use
|
||||||
|
Method = keys.NewString("method", "")
|
||||||
|
StatusCode = keys.NewString("status.code", "")
|
||||||
|
StatusMessage = keys.NewString("status.message", "")
|
||||||
|
RPCID = keys.NewString("id", "")
|
||||||
|
RPCDirection = keys.NewString("direction", "")
|
||||||
|
File = keys.NewString("file", "")
|
||||||
|
Directory = keys.New("directory", "")
|
||||||
|
URI = keys.New("URI", "")
|
||||||
|
Package = keys.NewString("package", "") // sorted comma-separated list of Package IDs
|
||||||
|
PackagePath = keys.NewString("package_path", "")
|
||||||
|
Query = keys.New("query", "")
|
||||||
|
Snapshot = keys.NewUInt64("snapshot", "")
|
||||||
|
Operation = keys.NewString("operation", "")
|
||||||
|
|
||||||
|
Position = keys.New("position", "")
|
||||||
|
Category = keys.NewString("category", "")
|
||||||
|
PackageCount = keys.NewInt("packages", "")
|
||||||
|
Files = keys.New("files", "")
|
||||||
|
Port = keys.NewInt("port", "")
|
||||||
|
Type = keys.New("type", "")
|
||||||
|
HoverKind = keys.NewString("hoverkind", "")
|
||||||
|
|
||||||
|
NewServer = keys.NewString("new_server", "A new server was added")
|
||||||
|
EndServer = keys.NewString("end_server", "A server was shut down")
|
||||||
|
|
||||||
|
ServerID = keys.NewString("server", "The server ID an event is related to")
|
||||||
|
Logfile = keys.NewString("logfile", "")
|
||||||
|
DebugAddress = keys.NewString("debug_address", "")
|
||||||
|
GoplsPath = keys.NewString("gopls_path", "")
|
||||||
|
ClientID = keys.NewString("client_id", "")
|
||||||
|
|
||||||
|
Level = keys.NewInt("level", "The logging level")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// create the stats we measure
|
||||||
|
Started = keys.NewInt64("started", "Count of started RPCs.")
|
||||||
|
ReceivedBytes = keys.NewInt64("received_bytes", "Bytes received.") //, unit.Bytes)
|
||||||
|
SentBytes = keys.NewInt64("sent_bytes", "Bytes sent.") //, unit.Bytes)
|
||||||
|
Latency = keys.NewFloat64("latency_ms", "Elapsed time in milliseconds") //, unit.Milliseconds)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Inbound = "in"
|
||||||
|
Outbound = "out"
|
||||||
|
)
|
|
@ -1,852 +0,0 @@
|
||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
// Binary package export.
|
|
||||||
// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go;
|
|
||||||
// see that file for specification of the format.
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"go/constant"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"math"
|
|
||||||
"math/big"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// If debugFormat is set, each integer and string value is preceded by a marker
|
|
||||||
// and position information in the encoding. This mechanism permits an importer
|
|
||||||
// to recognize immediately when it is out of sync. The importer recognizes this
|
|
||||||
// mode automatically (i.e., it can import export data produced with debugging
|
|
||||||
// support even if debugFormat is not set at the time of import). This mode will
|
|
||||||
// lead to massively larger export data (by a factor of 2 to 3) and should only
|
|
||||||
// be enabled during development and debugging.
|
|
||||||
//
|
|
||||||
// NOTE: This flag is the first flag to enable if importing dies because of
|
|
||||||
// (suspected) format errors, and whenever a change is made to the format.
|
|
||||||
const debugFormat = false // default: false
|
|
||||||
|
|
||||||
// Current export format version. Increase with each format change.
|
|
||||||
//
|
|
||||||
// Note: The latest binary (non-indexed) export format is at version 6.
|
|
||||||
// This exporter is still at level 4, but it doesn't matter since
|
|
||||||
// the binary importer can handle older versions just fine.
|
|
||||||
//
|
|
||||||
// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE
|
|
||||||
// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE
|
|
||||||
// 4: type name objects support type aliases, uses aliasTag
|
|
||||||
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
|
|
||||||
// 2: removed unused bool in ODCL export (compiler only)
|
|
||||||
// 1: header format change (more regular), export package for _ struct fields
|
|
||||||
// 0: Go1.7 encoding
|
|
||||||
const exportVersion = 4
|
|
||||||
|
|
||||||
// trackAllTypes enables cycle tracking for all types, not just named
|
|
||||||
// types. The existing compiler invariants assume that unnamed types
|
|
||||||
// that are not completely set up are not used, or else there are spurious
|
|
||||||
// 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).
|
|
||||||
// TODO(gri) enable and remove once issues caused by it are fixed
|
|
||||||
const trackAllTypes = false
|
|
||||||
|
|
||||||
type exporter struct {
|
|
||||||
fset *token.FileSet
|
|
||||||
out bytes.Buffer
|
|
||||||
|
|
||||||
// object -> index maps, indexed in order of serialization
|
|
||||||
strIndex map[string]int
|
|
||||||
pkgIndex map[*types.Package]int
|
|
||||||
typIndex map[types.Type]int
|
|
||||||
|
|
||||||
// position encoding
|
|
||||||
posInfoFormat bool
|
|
||||||
prevFile string
|
|
||||||
prevLine int
|
|
||||||
|
|
||||||
// debugging support
|
|
||||||
written int // bytes written
|
|
||||||
indent int // for trace
|
|
||||||
}
|
|
||||||
|
|
||||||
// internalError represents an error generated inside this package.
|
|
||||||
type internalError string
|
|
||||||
|
|
||||||
func (e internalError) Error() string { return "gcimporter: " + string(e) }
|
|
||||||
|
|
||||||
func internalErrorf(format string, args ...interface{}) error {
|
|
||||||
return internalError(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// BExportData returns binary export data for pkg.
|
|
||||||
// If no file set is provided, position info will be missing.
|
|
||||||
func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) {
|
|
||||||
if !debug {
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
if ierr, ok := e.(internalError); ok {
|
|
||||||
err = ierr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Not an internal error; panic again.
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
p := exporter{
|
|
||||||
fset: fset,
|
|
||||||
strIndex: map[string]int{"": 0}, // empty string is mapped to 0
|
|
||||||
pkgIndex: make(map[*types.Package]int),
|
|
||||||
typIndex: make(map[types.Type]int),
|
|
||||||
posInfoFormat: true, // TODO(gri) might become a flag, eventually
|
|
||||||
}
|
|
||||||
|
|
||||||
// write version info
|
|
||||||
// The version string must start with "version %d" where %d is the version
|
|
||||||
// number. Additional debugging information may follow after a blank; that
|
|
||||||
// text is ignored by the importer.
|
|
||||||
p.rawStringln(fmt.Sprintf("version %d", exportVersion))
|
|
||||||
var debug string
|
|
||||||
if debugFormat {
|
|
||||||
debug = "debug"
|
|
||||||
}
|
|
||||||
p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly
|
|
||||||
p.bool(trackAllTypes)
|
|
||||||
p.bool(p.posInfoFormat)
|
|
||||||
|
|
||||||
// --- generic export data ---
|
|
||||||
|
|
||||||
// populate type map with predeclared "known" types
|
|
||||||
for index, typ := range predeclared() {
|
|
||||||
p.typIndex[typ] = index
|
|
||||||
}
|
|
||||||
if len(p.typIndex) != len(predeclared()) {
|
|
||||||
return nil, internalError("duplicate entries in type map?")
|
|
||||||
}
|
|
||||||
|
|
||||||
// write package data
|
|
||||||
p.pkg(pkg, true)
|
|
||||||
if trace {
|
|
||||||
p.tracef("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// write objects
|
|
||||||
objcount := 0
|
|
||||||
scope := pkg.Scope()
|
|
||||||
for _, name := range scope.Names() {
|
|
||||||
if !token.IsExported(name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if trace {
|
|
||||||
p.tracef("\n")
|
|
||||||
}
|
|
||||||
p.obj(scope.Lookup(name))
|
|
||||||
objcount++
|
|
||||||
}
|
|
||||||
|
|
||||||
// indicate end of list
|
|
||||||
if trace {
|
|
||||||
p.tracef("\n")
|
|
||||||
}
|
|
||||||
p.tag(endTag)
|
|
||||||
|
|
||||||
// for self-verification only (redundant)
|
|
||||||
p.int(objcount)
|
|
||||||
|
|
||||||
if trace {
|
|
||||||
p.tracef("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- end of export data ---
|
|
||||||
|
|
||||||
return p.out.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) pkg(pkg *types.Package, emptypath bool) {
|
|
||||||
if pkg == nil {
|
|
||||||
panic(internalError("unexpected nil pkg"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we saw the package before, write its index (>= 0)
|
|
||||||
if i, ok := p.pkgIndex[pkg]; ok {
|
|
||||||
p.index('P', i)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, remember the package, write the package tag (< 0) and package data
|
|
||||||
if trace {
|
|
||||||
p.tracef("P%d = { ", len(p.pkgIndex))
|
|
||||||
defer p.tracef("} ")
|
|
||||||
}
|
|
||||||
p.pkgIndex[pkg] = len(p.pkgIndex)
|
|
||||||
|
|
||||||
p.tag(packageTag)
|
|
||||||
p.string(pkg.Name())
|
|
||||||
if emptypath {
|
|
||||||
p.string("")
|
|
||||||
} else {
|
|
||||||
p.string(pkg.Path())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) obj(obj types.Object) {
|
|
||||||
switch obj := obj.(type) {
|
|
||||||
case *types.Const:
|
|
||||||
p.tag(constTag)
|
|
||||||
p.pos(obj)
|
|
||||||
p.qualifiedName(obj)
|
|
||||||
p.typ(obj.Type())
|
|
||||||
p.value(obj.Val())
|
|
||||||
|
|
||||||
case *types.TypeName:
|
|
||||||
if obj.IsAlias() {
|
|
||||||
p.tag(aliasTag)
|
|
||||||
p.pos(obj)
|
|
||||||
p.qualifiedName(obj)
|
|
||||||
} else {
|
|
||||||
p.tag(typeTag)
|
|
||||||
}
|
|
||||||
p.typ(obj.Type())
|
|
||||||
|
|
||||||
case *types.Var:
|
|
||||||
p.tag(varTag)
|
|
||||||
p.pos(obj)
|
|
||||||
p.qualifiedName(obj)
|
|
||||||
p.typ(obj.Type())
|
|
||||||
|
|
||||||
case *types.Func:
|
|
||||||
p.tag(funcTag)
|
|
||||||
p.pos(obj)
|
|
||||||
p.qualifiedName(obj)
|
|
||||||
sig := obj.Type().(*types.Signature)
|
|
||||||
p.paramList(sig.Params(), sig.Variadic())
|
|
||||||
p.paramList(sig.Results(), false)
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(internalErrorf("unexpected object %v (%T)", obj, obj))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) pos(obj types.Object) {
|
|
||||||
if !p.posInfoFormat {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file, line := p.fileLine(obj)
|
|
||||||
if file == p.prevFile {
|
|
||||||
// common case: write line delta
|
|
||||||
// delta == 0 means different file or no line change
|
|
||||||
delta := line - p.prevLine
|
|
||||||
p.int(delta)
|
|
||||||
if delta == 0 {
|
|
||||||
p.int(-1) // -1 means no file change
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// different file
|
|
||||||
p.int(0)
|
|
||||||
// Encode filename as length of common prefix with previous
|
|
||||||
// filename, followed by (possibly empty) suffix. Filenames
|
|
||||||
// frequently share path prefixes, so this can save a lot
|
|
||||||
// of space and make export data size less dependent on file
|
|
||||||
// path length. The suffix is unlikely to be empty because
|
|
||||||
// file names tend to end in ".go".
|
|
||||||
n := commonPrefixLen(p.prevFile, file)
|
|
||||||
p.int(n) // n >= 0
|
|
||||||
p.string(file[n:]) // write suffix only
|
|
||||||
p.prevFile = file
|
|
||||||
p.int(line)
|
|
||||||
}
|
|
||||||
p.prevLine = line
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) fileLine(obj types.Object) (file string, line int) {
|
|
||||||
if p.fset != nil {
|
|
||||||
pos := p.fset.Position(obj.Pos())
|
|
||||||
file = pos.Filename
|
|
||||||
line = pos.Line
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func commonPrefixLen(a, b string) int {
|
|
||||||
if len(a) > len(b) {
|
|
||||||
a, b = b, a
|
|
||||||
}
|
|
||||||
// len(a) <= len(b)
|
|
||||||
i := 0
|
|
||||||
for i < len(a) && a[i] == b[i] {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) qualifiedName(obj types.Object) {
|
|
||||||
p.string(obj.Name())
|
|
||||||
p.pkg(obj.Pkg(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) typ(t types.Type) {
|
|
||||||
if t == nil {
|
|
||||||
panic(internalError("nil type"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Possible optimization: Anonymous pointer types *T where
|
|
||||||
// T is a named type are common. We could canonicalize all
|
|
||||||
// such types *T to a single type PT = *T. This would lead
|
|
||||||
// to at most one *T entry in typIndex, and all future *T's
|
|
||||||
// would be encoded as the respective index directly. Would
|
|
||||||
// save 1 byte (pointerTag) per *T and reduce the typIndex
|
|
||||||
// size (at the cost of a canonicalization map). We can do
|
|
||||||
// this later, without encoding format change.
|
|
||||||
|
|
||||||
// if we saw the type before, write its index (>= 0)
|
|
||||||
if i, ok := p.typIndex[t]; ok {
|
|
||||||
p.index('T', i)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, remember the type, write the type tag (< 0) and type data
|
|
||||||
if trackAllTypes {
|
|
||||||
if trace {
|
|
||||||
p.tracef("T%d = {>\n", len(p.typIndex))
|
|
||||||
defer p.tracef("<\n} ")
|
|
||||||
}
|
|
||||||
p.typIndex[t] = len(p.typIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.Named:
|
|
||||||
if !trackAllTypes {
|
|
||||||
// if we don't track all types, track named types now
|
|
||||||
p.typIndex[t] = len(p.typIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.tag(namedTag)
|
|
||||||
p.pos(t.Obj())
|
|
||||||
p.qualifiedName(t.Obj())
|
|
||||||
p.typ(t.Underlying())
|
|
||||||
if !types.IsInterface(t) {
|
|
||||||
p.assocMethods(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.Array:
|
|
||||||
p.tag(arrayTag)
|
|
||||||
p.int64(t.Len())
|
|
||||||
p.typ(t.Elem())
|
|
||||||
|
|
||||||
case *types.Slice:
|
|
||||||
p.tag(sliceTag)
|
|
||||||
p.typ(t.Elem())
|
|
||||||
|
|
||||||
case *dddSlice:
|
|
||||||
p.tag(dddTag)
|
|
||||||
p.typ(t.elem)
|
|
||||||
|
|
||||||
case *types.Struct:
|
|
||||||
p.tag(structTag)
|
|
||||||
p.fieldList(t)
|
|
||||||
|
|
||||||
case *types.Pointer:
|
|
||||||
p.tag(pointerTag)
|
|
||||||
p.typ(t.Elem())
|
|
||||||
|
|
||||||
case *types.Signature:
|
|
||||||
p.tag(signatureTag)
|
|
||||||
p.paramList(t.Params(), t.Variadic())
|
|
||||||
p.paramList(t.Results(), false)
|
|
||||||
|
|
||||||
case *types.Interface:
|
|
||||||
p.tag(interfaceTag)
|
|
||||||
p.iface(t)
|
|
||||||
|
|
||||||
case *types.Map:
|
|
||||||
p.tag(mapTag)
|
|
||||||
p.typ(t.Key())
|
|
||||||
p.typ(t.Elem())
|
|
||||||
|
|
||||||
case *types.Chan:
|
|
||||||
p.tag(chanTag)
|
|
||||||
p.int(int(3 - t.Dir())) // hack
|
|
||||||
p.typ(t.Elem())
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(internalErrorf("unexpected type %T: %s", t, t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) assocMethods(named *types.Named) {
|
|
||||||
// Sort methods (for determinism).
|
|
||||||
var methods []*types.Func
|
|
||||||
for i := 0; i < named.NumMethods(); i++ {
|
|
||||||
methods = append(methods, named.Method(i))
|
|
||||||
}
|
|
||||||
sort.Sort(methodsByName(methods))
|
|
||||||
|
|
||||||
p.int(len(methods))
|
|
||||||
|
|
||||||
if trace && methods != nil {
|
|
||||||
p.tracef("associated methods {>\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, m := range methods {
|
|
||||||
if trace && i > 0 {
|
|
||||||
p.tracef("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
p.pos(m)
|
|
||||||
name := m.Name()
|
|
||||||
p.string(name)
|
|
||||||
if !exported(name) {
|
|
||||||
p.pkg(m.Pkg(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
sig := m.Type().(*types.Signature)
|
|
||||||
p.paramList(types.NewTuple(sig.Recv()), false)
|
|
||||||
p.paramList(sig.Params(), sig.Variadic())
|
|
||||||
p.paramList(sig.Results(), false)
|
|
||||||
p.int(0) // dummy value for go:nointerface pragma - ignored by importer
|
|
||||||
}
|
|
||||||
|
|
||||||
if trace && methods != nil {
|
|
||||||
p.tracef("<\n} ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type methodsByName []*types.Func
|
|
||||||
|
|
||||||
func (x methodsByName) Len() int { return len(x) }
|
|
||||||
func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() }
|
|
||||||
|
|
||||||
func (p *exporter) fieldList(t *types.Struct) {
|
|
||||||
if trace && t.NumFields() > 0 {
|
|
||||||
p.tracef("fields {>\n")
|
|
||||||
defer p.tracef("<\n} ")
|
|
||||||
}
|
|
||||||
|
|
||||||
p.int(t.NumFields())
|
|
||||||
for i := 0; i < t.NumFields(); i++ {
|
|
||||||
if trace && i > 0 {
|
|
||||||
p.tracef("\n")
|
|
||||||
}
|
|
||||||
p.field(t.Field(i))
|
|
||||||
p.string(t.Tag(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) field(f *types.Var) {
|
|
||||||
if !f.IsField() {
|
|
||||||
panic(internalError("field expected"))
|
|
||||||
}
|
|
||||||
|
|
||||||
p.pos(f)
|
|
||||||
p.fieldName(f)
|
|
||||||
p.typ(f.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) iface(t *types.Interface) {
|
|
||||||
// TODO(gri): enable importer to load embedded interfaces,
|
|
||||||
// then emit Embeddeds and ExplicitMethods separately here.
|
|
||||||
p.int(0)
|
|
||||||
|
|
||||||
n := t.NumMethods()
|
|
||||||
if trace && n > 0 {
|
|
||||||
p.tracef("methods {>\n")
|
|
||||||
defer p.tracef("<\n} ")
|
|
||||||
}
|
|
||||||
p.int(n)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
if trace && i > 0 {
|
|
||||||
p.tracef("\n")
|
|
||||||
}
|
|
||||||
p.method(t.Method(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) method(m *types.Func) {
|
|
||||||
sig := m.Type().(*types.Signature)
|
|
||||||
if sig.Recv() == nil {
|
|
||||||
panic(internalError("method expected"))
|
|
||||||
}
|
|
||||||
|
|
||||||
p.pos(m)
|
|
||||||
p.string(m.Name())
|
|
||||||
if m.Name() != "_" && !token.IsExported(m.Name()) {
|
|
||||||
p.pkg(m.Pkg(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// interface method; no need to encode receiver.
|
|
||||||
p.paramList(sig.Params(), sig.Variadic())
|
|
||||||
p.paramList(sig.Results(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) fieldName(f *types.Var) {
|
|
||||||
name := f.Name()
|
|
||||||
|
|
||||||
if f.Anonymous() {
|
|
||||||
// anonymous field - we distinguish between 3 cases:
|
|
||||||
// 1) field name matches base type name and is exported
|
|
||||||
// 2) field name matches base type name and is not exported
|
|
||||||
// 3) field name doesn't match base type name (alias name)
|
|
||||||
bname := basetypeName(f.Type())
|
|
||||||
if name == bname {
|
|
||||||
if token.IsExported(name) {
|
|
||||||
name = "" // 1) we don't need to know the field name or package
|
|
||||||
} else {
|
|
||||||
name = "?" // 2) use unexported name "?" to force package export
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 3) indicate alias and export name as is
|
|
||||||
// (this requires an extra "@" but this is a rare case)
|
|
||||||
p.string("@")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.string(name)
|
|
||||||
if name != "" && !token.IsExported(name) {
|
|
||||||
p.pkg(f.Pkg(), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func basetypeName(typ types.Type) string {
|
|
||||||
switch typ := deref(typ).(type) {
|
|
||||||
case *types.Basic:
|
|
||||||
return typ.Name()
|
|
||||||
case *types.Named:
|
|
||||||
return typ.Obj().Name()
|
|
||||||
default:
|
|
||||||
return "" // unnamed type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) paramList(params *types.Tuple, variadic bool) {
|
|
||||||
// use negative length to indicate unnamed parameters
|
|
||||||
// (look at the first parameter only since either all
|
|
||||||
// names are present or all are absent)
|
|
||||||
n := params.Len()
|
|
||||||
if n > 0 && params.At(0).Name() == "" {
|
|
||||||
n = -n
|
|
||||||
}
|
|
||||||
p.int(n)
|
|
||||||
for i := 0; i < params.Len(); i++ {
|
|
||||||
q := params.At(i)
|
|
||||||
t := q.Type()
|
|
||||||
if variadic && i == params.Len()-1 {
|
|
||||||
t = &dddSlice{t.(*types.Slice).Elem()}
|
|
||||||
}
|
|
||||||
p.typ(t)
|
|
||||||
if n > 0 {
|
|
||||||
name := q.Name()
|
|
||||||
p.string(name)
|
|
||||||
if name != "_" {
|
|
||||||
p.pkg(q.Pkg(), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.string("") // no compiler-specific info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) value(x constant.Value) {
|
|
||||||
if trace {
|
|
||||||
p.tracef("= ")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch x.Kind() {
|
|
||||||
case constant.Bool:
|
|
||||||
tag := falseTag
|
|
||||||
if constant.BoolVal(x) {
|
|
||||||
tag = trueTag
|
|
||||||
}
|
|
||||||
p.tag(tag)
|
|
||||||
|
|
||||||
case constant.Int:
|
|
||||||
if v, exact := constant.Int64Val(x); exact {
|
|
||||||
// common case: x fits into an int64 - use compact encoding
|
|
||||||
p.tag(int64Tag)
|
|
||||||
p.int64(v)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// uncommon case: large x - use float encoding
|
|
||||||
// (powers of 2 will be encoded efficiently with exponent)
|
|
||||||
p.tag(floatTag)
|
|
||||||
p.float(constant.ToFloat(x))
|
|
||||||
|
|
||||||
case constant.Float:
|
|
||||||
p.tag(floatTag)
|
|
||||||
p.float(x)
|
|
||||||
|
|
||||||
case constant.Complex:
|
|
||||||
p.tag(complexTag)
|
|
||||||
p.float(constant.Real(x))
|
|
||||||
p.float(constant.Imag(x))
|
|
||||||
|
|
||||||
case constant.String:
|
|
||||||
p.tag(stringTag)
|
|
||||||
p.string(constant.StringVal(x))
|
|
||||||
|
|
||||||
case constant.Unknown:
|
|
||||||
// package contains type errors
|
|
||||||
p.tag(unknownTag)
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(internalErrorf("unexpected value %v (%T)", x, x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) float(x constant.Value) {
|
|
||||||
if x.Kind() != constant.Float {
|
|
||||||
panic(internalErrorf("unexpected constant %v, want float", x))
|
|
||||||
}
|
|
||||||
// extract sign (there is no -0)
|
|
||||||
sign := constant.Sign(x)
|
|
||||||
if sign == 0 {
|
|
||||||
// x == 0
|
|
||||||
p.int(0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// x != 0
|
|
||||||
|
|
||||||
var f big.Float
|
|
||||||
if v, exact := constant.Float64Val(x); exact {
|
|
||||||
// float64
|
|
||||||
f.SetFloat64(v)
|
|
||||||
} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int {
|
|
||||||
// TODO(gri): add big.Rat accessor to constant.Value.
|
|
||||||
r := valueToRat(num)
|
|
||||||
f.SetRat(r.Quo(r, valueToRat(denom)))
|
|
||||||
} else {
|
|
||||||
// Value too large to represent as a fraction => inaccessible.
|
|
||||||
// TODO(gri): add big.Float accessor to constant.Value.
|
|
||||||
f.SetFloat64(math.MaxFloat64) // FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract exponent such that 0.5 <= m < 1.0
|
|
||||||
var m big.Float
|
|
||||||
exp := f.MantExp(&m)
|
|
||||||
|
|
||||||
// extract mantissa as *big.Int
|
|
||||||
// - set exponent large enough so mant satisfies mant.IsInt()
|
|
||||||
// - get *big.Int from mant
|
|
||||||
m.SetMantExp(&m, int(m.MinPrec()))
|
|
||||||
mant, acc := m.Int(nil)
|
|
||||||
if acc != big.Exact {
|
|
||||||
panic(internalError("internal error"))
|
|
||||||
}
|
|
||||||
|
|
||||||
p.int(sign)
|
|
||||||
p.int(exp)
|
|
||||||
p.string(string(mant.Bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func valueToRat(x constant.Value) *big.Rat {
|
|
||||||
// Convert little-endian to big-endian.
|
|
||||||
// I can't believe this is necessary.
|
|
||||||
bytes := constant.Bytes(x)
|
|
||||||
for i := 0; i < len(bytes)/2; i++ {
|
|
||||||
bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
|
|
||||||
}
|
|
||||||
return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) bool(b bool) bool {
|
|
||||||
if trace {
|
|
||||||
p.tracef("[")
|
|
||||||
defer p.tracef("= %v] ", b)
|
|
||||||
}
|
|
||||||
|
|
||||||
x := 0
|
|
||||||
if b {
|
|
||||||
x = 1
|
|
||||||
}
|
|
||||||
p.int(x)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Low-level encoders
|
|
||||||
|
|
||||||
func (p *exporter) index(marker byte, index int) {
|
|
||||||
if index < 0 {
|
|
||||||
panic(internalError("invalid index < 0"))
|
|
||||||
}
|
|
||||||
if debugFormat {
|
|
||||||
p.marker('t')
|
|
||||||
}
|
|
||||||
if trace {
|
|
||||||
p.tracef("%c%d ", marker, index)
|
|
||||||
}
|
|
||||||
p.rawInt64(int64(index))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) tag(tag int) {
|
|
||||||
if tag >= 0 {
|
|
||||||
panic(internalError("invalid tag >= 0"))
|
|
||||||
}
|
|
||||||
if debugFormat {
|
|
||||||
p.marker('t')
|
|
||||||
}
|
|
||||||
if trace {
|
|
||||||
p.tracef("%s ", tagString[-tag])
|
|
||||||
}
|
|
||||||
p.rawInt64(int64(tag))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) int(x int) {
|
|
||||||
p.int64(int64(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) int64(x int64) {
|
|
||||||
if debugFormat {
|
|
||||||
p.marker('i')
|
|
||||||
}
|
|
||||||
if trace {
|
|
||||||
p.tracef("%d ", x)
|
|
||||||
}
|
|
||||||
p.rawInt64(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *exporter) string(s string) {
|
|
||||||
if debugFormat {
|
|
||||||
p.marker('s')
|
|
||||||
}
|
|
||||||
if trace {
|
|
||||||
p.tracef("%q ", s)
|
|
||||||
}
|
|
||||||
// if we saw the string before, write its index (>= 0)
|
|
||||||
// (the empty string is mapped to 0)
|
|
||||||
if i, ok := p.strIndex[s]; ok {
|
|
||||||
p.rawInt64(int64(i))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// otherwise, remember string and write its negative length and bytes
|
|
||||||
p.strIndex[s] = len(p.strIndex)
|
|
||||||
p.rawInt64(-int64(len(s)))
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
p.rawByte(s[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// marker emits a marker byte and position information which makes
|
|
||||||
// it easy for a reader to detect if it is "out of sync". Used for
|
|
||||||
// debugFormat format only.
|
|
||||||
func (p *exporter) marker(m byte) {
|
|
||||||
p.rawByte(m)
|
|
||||||
// Enable this for help tracking down the location
|
|
||||||
// of an incorrect marker when running in debugFormat.
|
|
||||||
if false && trace {
|
|
||||||
p.tracef("#%d ", p.written)
|
|
||||||
}
|
|
||||||
p.rawInt64(int64(p.written))
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawInt64 should only be used by low-level encoders.
|
|
||||||
func (p *exporter) rawInt64(x int64) {
|
|
||||||
var tmp [binary.MaxVarintLen64]byte
|
|
||||||
n := binary.PutVarint(tmp[:], x)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
p.rawByte(tmp[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawStringln should only be used to emit the initial version string.
|
|
||||||
func (p *exporter) rawStringln(s string) {
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
p.rawByte(s[i])
|
|
||||||
}
|
|
||||||
p.rawByte('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawByte is the bottleneck interface to write to p.out.
|
|
||||||
// rawByte escapes b as follows (any encoding does that
|
|
||||||
// hides '$'):
|
|
||||||
//
|
|
||||||
// '$' => '|' 'S'
|
|
||||||
// '|' => '|' '|'
|
|
||||||
//
|
|
||||||
// Necessary so other tools can find the end of the
|
|
||||||
// export data by searching for "$$".
|
|
||||||
// rawByte should only be used by low-level encoders.
|
|
||||||
func (p *exporter) rawByte(b byte) {
|
|
||||||
switch b {
|
|
||||||
case '$':
|
|
||||||
// write '$' as '|' 'S'
|
|
||||||
b = 'S'
|
|
||||||
fallthrough
|
|
||||||
case '|':
|
|
||||||
// write '|' as '|' '|'
|
|
||||||
p.out.WriteByte('|')
|
|
||||||
p.written++
|
|
||||||
}
|
|
||||||
p.out.WriteByte(b)
|
|
||||||
p.written++
|
|
||||||
}
|
|
||||||
|
|
||||||
// tracef is like fmt.Printf but it rewrites the format string
|
|
||||||
// to take care of indentation.
|
|
||||||
func (p *exporter) tracef(format string, args ...interface{}) {
|
|
||||||
if strings.ContainsAny(format, "<>\n") {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for i := 0; i < len(format); i++ {
|
|
||||||
// no need to deal with runes
|
|
||||||
ch := format[i]
|
|
||||||
switch ch {
|
|
||||||
case '>':
|
|
||||||
p.indent++
|
|
||||||
continue
|
|
||||||
case '<':
|
|
||||||
p.indent--
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buf.WriteByte(ch)
|
|
||||||
if ch == '\n' {
|
|
||||||
for j := p.indent; j > 0; j-- {
|
|
||||||
buf.WriteString(". ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
format = buf.String()
|
|
||||||
}
|
|
||||||
fmt.Printf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugging support.
|
|
||||||
// (tagString is only used when tracing is enabled)
|
|
||||||
var tagString = [...]string{
|
|
||||||
// Packages
|
|
||||||
-packageTag: "package",
|
|
||||||
|
|
||||||
// Types
|
|
||||||
-namedTag: "named type",
|
|
||||||
-arrayTag: "array",
|
|
||||||
-sliceTag: "slice",
|
|
||||||
-dddTag: "ddd",
|
|
||||||
-structTag: "struct",
|
|
||||||
-pointerTag: "pointer",
|
|
||||||
-signatureTag: "signature",
|
|
||||||
-interfaceTag: "interface",
|
|
||||||
-mapTag: "map",
|
|
||||||
-chanTag: "chan",
|
|
||||||
|
|
||||||
// Values
|
|
||||||
-falseTag: "false",
|
|
||||||
-trueTag: "true",
|
|
||||||
-int64Tag: "int64",
|
|
||||||
-floatTag: "float",
|
|
||||||
-fractionTag: "fraction",
|
|
||||||
-complexTag: "complex",
|
|
||||||
-stringTag: "string",
|
|
||||||
-unknownTag: "unknown",
|
|
||||||
|
|
||||||
// Type aliases
|
|
||||||
-aliasTag: "alias",
|
|
||||||
}
|
|
|
@ -2,340 +2,24 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go.
|
// This file contains the remaining vestiges of
|
||||||
|
// $GOROOT/src/go/internal/gcimporter/bimport.go.
|
||||||
|
|
||||||
package gcimporter
|
package gcimporter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type importer struct {
|
|
||||||
imports map[string]*types.Package
|
|
||||||
data []byte
|
|
||||||
importpath string
|
|
||||||
buf []byte // for reading strings
|
|
||||||
version int // export format version
|
|
||||||
|
|
||||||
// object lists
|
|
||||||
strList []string // in order of appearance
|
|
||||||
pathList []string // in order of appearance
|
|
||||||
pkgList []*types.Package // in order of appearance
|
|
||||||
typList []types.Type // in order of appearance
|
|
||||||
interfaceList []*types.Interface // for delayed completion only
|
|
||||||
trackAllTypes bool
|
|
||||||
|
|
||||||
// position encoding
|
|
||||||
posInfoFormat bool
|
|
||||||
prevFile string
|
|
||||||
prevLine int
|
|
||||||
fake fakeFileSet
|
|
||||||
|
|
||||||
// debugging support
|
|
||||||
debugFormat bool
|
|
||||||
read int // bytes read
|
|
||||||
}
|
|
||||||
|
|
||||||
// BImportData imports a package from the serialized package data
|
|
||||||
// and returns the number of bytes consumed and a reference to the package.
|
|
||||||
// If the export data version is not recognized or the format is otherwise
|
|
||||||
// compromised, an error is returned.
|
|
||||||
func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
|
|
||||||
// catch panics and return them as errors
|
|
||||||
const currentVersion = 6
|
|
||||||
version := -1 // unknown version
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
// Return a (possibly nil or incomplete) package unchanged (see #16088).
|
|
||||||
if version > currentVersion {
|
|
||||||
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
p := importer{
|
|
||||||
imports: imports,
|
|
||||||
data: data,
|
|
||||||
importpath: path,
|
|
||||||
version: version,
|
|
||||||
strList: []string{""}, // empty string is mapped to 0
|
|
||||||
pathList: []string{""}, // empty string is mapped to 0
|
|
||||||
fake: fakeFileSet{
|
|
||||||
fset: fset,
|
|
||||||
files: make(map[string]*fileInfo),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
defer p.fake.setLines() // set lines for files in fset
|
|
||||||
|
|
||||||
// read version info
|
|
||||||
var versionstr string
|
|
||||||
if b := p.rawByte(); b == 'c' || b == 'd' {
|
|
||||||
// Go1.7 encoding; first byte encodes low-level
|
|
||||||
// encoding format (compact vs debug).
|
|
||||||
// For backward-compatibility only (avoid problems with
|
|
||||||
// old installed packages). Newly compiled packages use
|
|
||||||
// the extensible format string.
|
|
||||||
// TODO(gri) Remove this support eventually; after Go1.8.
|
|
||||||
if b == 'd' {
|
|
||||||
p.debugFormat = true
|
|
||||||
}
|
|
||||||
p.trackAllTypes = p.rawByte() == 'a'
|
|
||||||
p.posInfoFormat = p.int() != 0
|
|
||||||
versionstr = p.string()
|
|
||||||
if versionstr == "v1" {
|
|
||||||
version = 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Go1.8 extensible encoding
|
|
||||||
// read version string and extract version number (ignore anything after the version number)
|
|
||||||
versionstr = p.rawStringln(b)
|
|
||||||
if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" {
|
|
||||||
if v, err := strconv.Atoi(s[1]); err == nil && v > 0 {
|
|
||||||
version = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.version = version
|
|
||||||
|
|
||||||
// read version specific flags - extend as necessary
|
|
||||||
switch p.version {
|
|
||||||
// case currentVersion:
|
|
||||||
// ...
|
|
||||||
// fallthrough
|
|
||||||
case currentVersion, 5, 4, 3, 2, 1:
|
|
||||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
|
||||||
p.trackAllTypes = p.int() != 0
|
|
||||||
p.posInfoFormat = p.int() != 0
|
|
||||||
case 0:
|
|
||||||
// Go1.7 encoding format - nothing to do here
|
|
||||||
default:
|
|
||||||
errorf("unknown bexport format version %d (%q)", p.version, versionstr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- generic export data ---
|
|
||||||
|
|
||||||
// populate typList with predeclared "known" types
|
|
||||||
p.typList = append(p.typList, predeclared()...)
|
|
||||||
|
|
||||||
// read package data
|
|
||||||
pkg = p.pkg()
|
|
||||||
|
|
||||||
// read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go)
|
|
||||||
objcount := 0
|
|
||||||
for {
|
|
||||||
tag := p.tagOrIndex()
|
|
||||||
if tag == endTag {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.obj(tag)
|
|
||||||
objcount++
|
|
||||||
}
|
|
||||||
|
|
||||||
// self-verification
|
|
||||||
if count := p.int(); count != objcount {
|
|
||||||
errorf("got %d objects; want %d", objcount, count)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore compiler-specific import data
|
|
||||||
|
|
||||||
// complete interfaces
|
|
||||||
// TODO(gri) re-investigate if we still need to do this in a delayed fashion
|
|
||||||
for _, typ := range p.interfaceList {
|
|
||||||
typ.Complete()
|
|
||||||
}
|
|
||||||
|
|
||||||
// record all referenced packages as imports
|
|
||||||
list := append(([]*types.Package)(nil), p.pkgList[1:]...)
|
|
||||||
sort.Sort(byPath(list))
|
|
||||||
pkg.SetImports(list)
|
|
||||||
|
|
||||||
// package was imported completely and without errors
|
|
||||||
pkg.MarkComplete()
|
|
||||||
|
|
||||||
return p.read, pkg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorf(format string, args ...interface{}) {
|
func errorf(format string, args ...interface{}) {
|
||||||
panic(fmt.Sprintf(format, args...))
|
panic(fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *importer) pkg() *types.Package {
|
|
||||||
// if the package was seen before, i is its index (>= 0)
|
|
||||||
i := p.tagOrIndex()
|
|
||||||
if i >= 0 {
|
|
||||||
return p.pkgList[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, i is the package tag (< 0)
|
|
||||||
if i != packageTag {
|
|
||||||
errorf("unexpected package tag %d version %d", i, p.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read package data
|
|
||||||
name := p.string()
|
|
||||||
var path string
|
|
||||||
if p.version >= 5 {
|
|
||||||
path = p.path()
|
|
||||||
} else {
|
|
||||||
path = p.string()
|
|
||||||
}
|
|
||||||
if p.version >= 6 {
|
|
||||||
p.int() // package height; unused by go/types
|
|
||||||
}
|
|
||||||
|
|
||||||
// we should never see an empty package name
|
|
||||||
if name == "" {
|
|
||||||
errorf("empty package name in import")
|
|
||||||
}
|
|
||||||
|
|
||||||
// an empty path denotes the package we are currently importing;
|
|
||||||
// it must be the first package we see
|
|
||||||
if (path == "") != (len(p.pkgList) == 0) {
|
|
||||||
errorf("package path %q for pkg index %d", path, len(p.pkgList))
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the package was imported before, use that one; otherwise create a new one
|
|
||||||
if path == "" {
|
|
||||||
path = p.importpath
|
|
||||||
}
|
|
||||||
pkg := p.imports[path]
|
|
||||||
if pkg == nil {
|
|
||||||
pkg = types.NewPackage(path, name)
|
|
||||||
p.imports[path] = pkg
|
|
||||||
} else if pkg.Name() != name {
|
|
||||||
errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path)
|
|
||||||
}
|
|
||||||
p.pkgList = append(p.pkgList, pkg)
|
|
||||||
|
|
||||||
return pkg
|
|
||||||
}
|
|
||||||
|
|
||||||
// objTag returns the tag value for each object kind.
|
|
||||||
func objTag(obj types.Object) int {
|
|
||||||
switch obj.(type) {
|
|
||||||
case *types.Const:
|
|
||||||
return constTag
|
|
||||||
case *types.TypeName:
|
|
||||||
return typeTag
|
|
||||||
case *types.Var:
|
|
||||||
return varTag
|
|
||||||
case *types.Func:
|
|
||||||
return funcTag
|
|
||||||
default:
|
|
||||||
errorf("unexpected object: %v (%T)", obj, obj) // panics
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sameObj(a, b types.Object) bool {
|
|
||||||
// Because unnamed types are not canonicalized, we cannot simply compare types for
|
|
||||||
// (pointer) identity.
|
|
||||||
// Ideally we'd check equality of constant values as well, but this is good enough.
|
|
||||||
return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) declare(obj types.Object) {
|
|
||||||
pkg := obj.Pkg()
|
|
||||||
if alt := pkg.Scope().Insert(obj); alt != nil {
|
|
||||||
// This can only trigger if we import a (non-type) object a second time.
|
|
||||||
// Excluding type aliases, this cannot happen because 1) we only import a package
|
|
||||||
// once; and b) we ignore compiler-specific export data which may contain
|
|
||||||
// functions whose inlined function bodies refer to other functions that
|
|
||||||
// were already imported.
|
|
||||||
// However, type aliases require reexporting the original type, so we need
|
|
||||||
// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
|
|
||||||
// method importer.obj, switch case importing functions).
|
|
||||||
// TODO(gri) review/update this comment once the gc compiler handles type aliases.
|
|
||||||
if !sameObj(obj, alt) {
|
|
||||||
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) obj(tag int) {
|
|
||||||
switch tag {
|
|
||||||
case constTag:
|
|
||||||
pos := p.pos()
|
|
||||||
pkg, name := p.qualifiedName()
|
|
||||||
typ := p.typ(nil, nil)
|
|
||||||
val := p.value()
|
|
||||||
p.declare(types.NewConst(pos, pkg, name, typ, val))
|
|
||||||
|
|
||||||
case aliasTag:
|
|
||||||
// TODO(gri) verify type alias hookup is correct
|
|
||||||
pos := p.pos()
|
|
||||||
pkg, name := p.qualifiedName()
|
|
||||||
typ := p.typ(nil, nil)
|
|
||||||
p.declare(types.NewTypeName(pos, pkg, name, typ))
|
|
||||||
|
|
||||||
case typeTag:
|
|
||||||
p.typ(nil, nil)
|
|
||||||
|
|
||||||
case varTag:
|
|
||||||
pos := p.pos()
|
|
||||||
pkg, name := p.qualifiedName()
|
|
||||||
typ := p.typ(nil, nil)
|
|
||||||
p.declare(types.NewVar(pos, pkg, name, typ))
|
|
||||||
|
|
||||||
case funcTag:
|
|
||||||
pos := p.pos()
|
|
||||||
pkg, name := p.qualifiedName()
|
|
||||||
params, isddd := p.paramList()
|
|
||||||
result, _ := p.paramList()
|
|
||||||
sig := types.NewSignature(nil, params, result, isddd)
|
|
||||||
p.declare(types.NewFunc(pos, pkg, name, sig))
|
|
||||||
|
|
||||||
default:
|
|
||||||
errorf("unexpected object tag %d", tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
|
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
|
||||||
|
|
||||||
func (p *importer) pos() token.Pos {
|
|
||||||
if !p.posInfoFormat {
|
|
||||||
return token.NoPos
|
|
||||||
}
|
|
||||||
|
|
||||||
file := p.prevFile
|
|
||||||
line := p.prevLine
|
|
||||||
delta := p.int()
|
|
||||||
line += delta
|
|
||||||
if p.version >= 5 {
|
|
||||||
if delta == deltaNewFile {
|
|
||||||
if n := p.int(); n >= 0 {
|
|
||||||
// file changed
|
|
||||||
file = p.path()
|
|
||||||
line = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if delta == 0 {
|
|
||||||
if n := p.int(); n >= 0 {
|
|
||||||
// file changed
|
|
||||||
file = p.prevFile[:n] + p.string()
|
|
||||||
line = p.int()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.prevFile = file
|
|
||||||
p.prevLine = line
|
|
||||||
|
|
||||||
return p.fake.pos(file, line, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synthesize a token.Pos
|
// Synthesize a token.Pos
|
||||||
type fakeFileSet struct {
|
type fakeFileSet struct {
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
|
@ -389,205 +73,6 @@ var (
|
||||||
fakeLinesOnce sync.Once
|
fakeLinesOnce sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
|
|
||||||
name = p.string()
|
|
||||||
pkg = p.pkg()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) record(t types.Type) {
|
|
||||||
p.typList = append(p.typList, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A dddSlice is a types.Type representing ...T parameters.
|
|
||||||
// It only appears for parameter types and does not escape
|
|
||||||
// the importer.
|
|
||||||
type dddSlice struct {
|
|
||||||
elem types.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *dddSlice) Underlying() types.Type { return t }
|
|
||||||
func (t *dddSlice) String() string { return "..." + t.elem.String() }
|
|
||||||
|
|
||||||
// parent is the package which declared the type; parent == nil means
|
|
||||||
// the package currently imported. The parent package is needed for
|
|
||||||
// exported struct fields and interface methods which don't contain
|
|
||||||
// explicit package information in the export data.
|
|
||||||
//
|
|
||||||
// A non-nil tname is used as the "owner" of the result type; i.e.,
|
|
||||||
// the result type is the underlying type of tname. tname is used
|
|
||||||
// to give interface methods a named receiver type where possible.
|
|
||||||
func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type {
|
|
||||||
// if the type was seen before, i is its index (>= 0)
|
|
||||||
i := p.tagOrIndex()
|
|
||||||
if i >= 0 {
|
|
||||||
return p.typList[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, i is the type tag (< 0)
|
|
||||||
switch i {
|
|
||||||
case namedTag:
|
|
||||||
// read type object
|
|
||||||
pos := p.pos()
|
|
||||||
parent, name := p.qualifiedName()
|
|
||||||
scope := parent.Scope()
|
|
||||||
obj := scope.Lookup(name)
|
|
||||||
|
|
||||||
// if the object doesn't exist yet, create and insert it
|
|
||||||
if obj == nil {
|
|
||||||
obj = types.NewTypeName(pos, parent, name, nil)
|
|
||||||
scope.Insert(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := obj.(*types.TypeName); !ok {
|
|
||||||
errorf("pkg = %s, name = %s => %s", parent, name, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// associate new named type with obj if it doesn't exist yet
|
|
||||||
t0 := types.NewNamed(obj.(*types.TypeName), nil, nil)
|
|
||||||
|
|
||||||
// but record the existing type, if any
|
|
||||||
tname := obj.Type().(*types.Named) // tname is either t0 or the existing type
|
|
||||||
p.record(tname)
|
|
||||||
|
|
||||||
// read underlying type
|
|
||||||
t0.SetUnderlying(p.typ(parent, t0))
|
|
||||||
|
|
||||||
// interfaces don't have associated methods
|
|
||||||
if types.IsInterface(t0) {
|
|
||||||
return tname
|
|
||||||
}
|
|
||||||
|
|
||||||
// read associated methods
|
|
||||||
for i := p.int(); i > 0; i-- {
|
|
||||||
// TODO(gri) replace this with something closer to fieldName
|
|
||||||
pos := p.pos()
|
|
||||||
name := p.string()
|
|
||||||
if !exported(name) {
|
|
||||||
p.pkg()
|
|
||||||
}
|
|
||||||
|
|
||||||
recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
|
|
||||||
params, isddd := p.paramList()
|
|
||||||
result, _ := p.paramList()
|
|
||||||
p.int() // go:nointerface pragma - discarded
|
|
||||||
|
|
||||||
sig := types.NewSignature(recv.At(0), params, result, isddd)
|
|
||||||
t0.AddMethod(types.NewFunc(pos, parent, name, sig))
|
|
||||||
}
|
|
||||||
|
|
||||||
return tname
|
|
||||||
|
|
||||||
case arrayTag:
|
|
||||||
t := new(types.Array)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
n := p.int64()
|
|
||||||
*t = *types.NewArray(p.typ(parent, nil), n)
|
|
||||||
return t
|
|
||||||
|
|
||||||
case sliceTag:
|
|
||||||
t := new(types.Slice)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
*t = *types.NewSlice(p.typ(parent, nil))
|
|
||||||
return t
|
|
||||||
|
|
||||||
case dddTag:
|
|
||||||
t := new(dddSlice)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.elem = p.typ(parent, nil)
|
|
||||||
return t
|
|
||||||
|
|
||||||
case structTag:
|
|
||||||
t := new(types.Struct)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
*t = *types.NewStruct(p.fieldList(parent))
|
|
||||||
return t
|
|
||||||
|
|
||||||
case pointerTag:
|
|
||||||
t := new(types.Pointer)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
*t = *types.NewPointer(p.typ(parent, nil))
|
|
||||||
return t
|
|
||||||
|
|
||||||
case signatureTag:
|
|
||||||
t := new(types.Signature)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
params, isddd := p.paramList()
|
|
||||||
result, _ := p.paramList()
|
|
||||||
*t = *types.NewSignature(nil, params, result, isddd)
|
|
||||||
return t
|
|
||||||
|
|
||||||
case interfaceTag:
|
|
||||||
// Create a dummy entry in the type list. This is safe because we
|
|
||||||
// cannot expect the interface type to appear in a cycle, as any
|
|
||||||
// such cycle must contain a named type which would have been
|
|
||||||
// first defined earlier.
|
|
||||||
// TODO(gri) Is this still true now that we have type aliases?
|
|
||||||
// See issue #23225.
|
|
||||||
n := len(p.typList)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
var embeddeds []types.Type
|
|
||||||
for n := p.int(); n > 0; n-- {
|
|
||||||
p.pos()
|
|
||||||
embeddeds = append(embeddeds, p.typ(parent, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
t := newInterface(p.methodList(parent, tname), embeddeds)
|
|
||||||
p.interfaceList = append(p.interfaceList, t)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.typList[n] = t
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
|
|
||||||
case mapTag:
|
|
||||||
t := new(types.Map)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
key := p.typ(parent, nil)
|
|
||||||
val := p.typ(parent, nil)
|
|
||||||
*t = *types.NewMap(key, val)
|
|
||||||
return t
|
|
||||||
|
|
||||||
case chanTag:
|
|
||||||
t := new(types.Chan)
|
|
||||||
if p.trackAllTypes {
|
|
||||||
p.record(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := chanDir(p.int())
|
|
||||||
val := p.typ(parent, nil)
|
|
||||||
*t = *types.NewChan(dir, val)
|
|
||||||
return t
|
|
||||||
|
|
||||||
default:
|
|
||||||
errorf("unexpected type tag %d", i) // panics
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func chanDir(d int) types.ChanDir {
|
func chanDir(d int) types.ChanDir {
|
||||||
// tag values must match the constants in cmd/compile/internal/gc/go.go
|
// tag values must match the constants in cmd/compile/internal/gc/go.go
|
||||||
switch d {
|
switch d {
|
||||||
|
@ -603,394 +88,6 @@ func chanDir(d int) types.ChanDir {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) {
|
|
||||||
if n := p.int(); n > 0 {
|
|
||||||
fields = make([]*types.Var, n)
|
|
||||||
tags = make([]string, n)
|
|
||||||
for i := range fields {
|
|
||||||
fields[i], tags[i] = p.field(parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) field(parent *types.Package) (*types.Var, string) {
|
|
||||||
pos := p.pos()
|
|
||||||
pkg, name, alias := p.fieldName(parent)
|
|
||||||
typ := p.typ(parent, nil)
|
|
||||||
tag := p.string()
|
|
||||||
|
|
||||||
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:
|
|
||||||
errorf("named base type expected")
|
|
||||||
}
|
|
||||||
anonymous = true
|
|
||||||
} else if alias {
|
|
||||||
// anonymous field: we have an explicit name because it's an alias
|
|
||||||
anonymous = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.NewField(pos, pkg, name, typ, anonymous), tag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) {
|
|
||||||
if n := p.int(); n > 0 {
|
|
||||||
methods = make([]*types.Func, n)
|
|
||||||
for i := range methods {
|
|
||||||
methods[i] = p.method(parent, baseType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func {
|
|
||||||
pos := p.pos()
|
|
||||||
pkg, name, _ := p.fieldName(parent)
|
|
||||||
// If we don't have a baseType, use a nil receiver.
|
|
||||||
// A receiver using the actual interface type (which
|
|
||||||
// we don't know yet) will be filled in when we call
|
|
||||||
// types.Interface.Complete.
|
|
||||||
var recv *types.Var
|
|
||||||
if baseType != nil {
|
|
||||||
recv = types.NewVar(token.NoPos, parent, "", baseType)
|
|
||||||
}
|
|
||||||
params, isddd := p.paramList()
|
|
||||||
result, _ := p.paramList()
|
|
||||||
sig := types.NewSignature(recv, params, result, isddd)
|
|
||||||
return types.NewFunc(pos, pkg, name, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
|
|
||||||
name = p.string()
|
|
||||||
pkg = parent
|
|
||||||
if pkg == nil {
|
|
||||||
// use the imported package instead
|
|
||||||
pkg = p.pkgList[0]
|
|
||||||
}
|
|
||||||
if p.version == 0 && name == "_" {
|
|
||||||
// version 0 didn't export a package for _ fields
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch name {
|
|
||||||
case "":
|
|
||||||
// 1) field name matches base type name and is exported: nothing to do
|
|
||||||
case "?":
|
|
||||||
// 2) field name matches base type name and is not exported: need package
|
|
||||||
name = ""
|
|
||||||
pkg = p.pkg()
|
|
||||||
case "@":
|
|
||||||
// 3) field name doesn't match type name (alias)
|
|
||||||
name = p.string()
|
|
||||||
alias = true
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
if !exported(name) {
|
|
||||||
pkg = p.pkg()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) paramList() (*types.Tuple, bool) {
|
|
||||||
n := p.int()
|
|
||||||
if n == 0 {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
// negative length indicates unnamed parameters
|
|
||||||
named := true
|
|
||||||
if n < 0 {
|
|
||||||
n = -n
|
|
||||||
named = false
|
|
||||||
}
|
|
||||||
// n > 0
|
|
||||||
params := make([]*types.Var, n)
|
|
||||||
isddd := false
|
|
||||||
for i := range params {
|
|
||||||
params[i], isddd = p.param(named)
|
|
||||||
}
|
|
||||||
return types.NewTuple(params...), isddd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) param(named bool) (*types.Var, bool) {
|
|
||||||
t := p.typ(nil, nil)
|
|
||||||
td, isddd := t.(*dddSlice)
|
|
||||||
if isddd {
|
|
||||||
t = types.NewSlice(td.elem)
|
|
||||||
}
|
|
||||||
|
|
||||||
var pkg *types.Package
|
|
||||||
var name string
|
|
||||||
if named {
|
|
||||||
name = p.string()
|
|
||||||
if name == "" {
|
|
||||||
errorf("expected named parameter")
|
|
||||||
}
|
|
||||||
if name != "_" {
|
|
||||||
pkg = p.pkg()
|
|
||||||
}
|
|
||||||
if i := strings.Index(name, "·"); i > 0 {
|
|
||||||
name = name[:i] // cut off gc-specific parameter numbering
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// read and discard compiler-specific info
|
|
||||||
p.string()
|
|
||||||
|
|
||||||
return types.NewVar(token.NoPos, pkg, name, t), isddd
|
|
||||||
}
|
|
||||||
|
|
||||||
func exported(name string) bool {
|
|
||||||
ch, _ := utf8.DecodeRuneInString(name)
|
|
||||||
return unicode.IsUpper(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) value() constant.Value {
|
|
||||||
switch tag := p.tagOrIndex(); tag {
|
|
||||||
case falseTag:
|
|
||||||
return constant.MakeBool(false)
|
|
||||||
case trueTag:
|
|
||||||
return constant.MakeBool(true)
|
|
||||||
case int64Tag:
|
|
||||||
return constant.MakeInt64(p.int64())
|
|
||||||
case floatTag:
|
|
||||||
return p.float()
|
|
||||||
case complexTag:
|
|
||||||
re := p.float()
|
|
||||||
im := p.float()
|
|
||||||
return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
|
|
||||||
case stringTag:
|
|
||||||
return constant.MakeString(p.string())
|
|
||||||
case unknownTag:
|
|
||||||
return constant.MakeUnknown()
|
|
||||||
default:
|
|
||||||
errorf("unexpected value tag %d", tag) // panics
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) float() constant.Value {
|
|
||||||
sign := p.int()
|
|
||||||
if sign == 0 {
|
|
||||||
return constant.MakeInt64(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
exp := p.int()
|
|
||||||
mant := []byte(p.string()) // big endian
|
|
||||||
|
|
||||||
// remove leading 0's if any
|
|
||||||
for len(mant) > 0 && mant[0] == 0 {
|
|
||||||
mant = mant[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert to little endian
|
|
||||||
// TODO(gri) go/constant should have a more direct conversion function
|
|
||||||
// (e.g., once it supports a big.Float based implementation)
|
|
||||||
for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
mant[i], mant[j] = mant[j], mant[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust exponent (constant.MakeFromBytes creates an integer value,
|
|
||||||
// but mant represents the mantissa bits such that 0.5 <= mant < 1.0)
|
|
||||||
exp -= len(mant) << 3
|
|
||||||
if len(mant) > 0 {
|
|
||||||
for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 {
|
|
||||||
exp++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
x := constant.MakeFromBytes(mant)
|
|
||||||
switch {
|
|
||||||
case exp < 0:
|
|
||||||
d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
|
|
||||||
x = constant.BinaryOp(x, token.QUO, d)
|
|
||||||
case exp > 0:
|
|
||||||
x = constant.Shift(x, token.SHL, uint(exp))
|
|
||||||
}
|
|
||||||
|
|
||||||
if sign < 0 {
|
|
||||||
x = constant.UnaryOp(token.SUB, x, 0)
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Low-level decoders
|
|
||||||
|
|
||||||
func (p *importer) tagOrIndex() int {
|
|
||||||
if p.debugFormat {
|
|
||||||
p.marker('t')
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(p.rawInt64())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) int() int {
|
|
||||||
x := p.int64()
|
|
||||||
if int64(int(x)) != x {
|
|
||||||
errorf("exported integer too large")
|
|
||||||
}
|
|
||||||
return int(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) int64() int64 {
|
|
||||||
if p.debugFormat {
|
|
||||||
p.marker('i')
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.rawInt64()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) path() string {
|
|
||||||
if p.debugFormat {
|
|
||||||
p.marker('p')
|
|
||||||
}
|
|
||||||
// if the path was seen before, i is its index (>= 0)
|
|
||||||
// (the empty string is at index 0)
|
|
||||||
i := p.rawInt64()
|
|
||||||
if i >= 0 {
|
|
||||||
return p.pathList[i]
|
|
||||||
}
|
|
||||||
// otherwise, i is the negative path length (< 0)
|
|
||||||
a := make([]string, -i)
|
|
||||||
for n := range a {
|
|
||||||
a[n] = p.string()
|
|
||||||
}
|
|
||||||
s := strings.Join(a, "/")
|
|
||||||
p.pathList = append(p.pathList, s)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) string() string {
|
|
||||||
if p.debugFormat {
|
|
||||||
p.marker('s')
|
|
||||||
}
|
|
||||||
// if the string was seen before, i is its index (>= 0)
|
|
||||||
// (the empty string is at index 0)
|
|
||||||
i := p.rawInt64()
|
|
||||||
if i >= 0 {
|
|
||||||
return p.strList[i]
|
|
||||||
}
|
|
||||||
// otherwise, i is the negative string length (< 0)
|
|
||||||
if n := int(-i); n <= cap(p.buf) {
|
|
||||||
p.buf = p.buf[:n]
|
|
||||||
} else {
|
|
||||||
p.buf = make([]byte, n)
|
|
||||||
}
|
|
||||||
for i := range p.buf {
|
|
||||||
p.buf[i] = p.rawByte()
|
|
||||||
}
|
|
||||||
s := string(p.buf)
|
|
||||||
p.strList = append(p.strList, s)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *importer) marker(want byte) {
|
|
||||||
if got := p.rawByte(); got != want {
|
|
||||||
errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
|
|
||||||
}
|
|
||||||
|
|
||||||
pos := p.read
|
|
||||||
if n := int(p.rawInt64()); n != pos {
|
|
||||||
errorf("incorrect position: got %d; want %d", n, pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawInt64 should only be used by low-level decoders.
|
|
||||||
func (p *importer) rawInt64() int64 {
|
|
||||||
i, err := binary.ReadVarint(p)
|
|
||||||
if err != nil {
|
|
||||||
errorf("read error: %v", err)
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawStringln should only be used to read the initial version string.
|
|
||||||
func (p *importer) rawStringln(b byte) string {
|
|
||||||
p.buf = p.buf[:0]
|
|
||||||
for b != '\n' {
|
|
||||||
p.buf = append(p.buf, b)
|
|
||||||
b = p.rawByte()
|
|
||||||
}
|
|
||||||
return string(p.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// needed for binary.ReadVarint in rawInt64
|
|
||||||
func (p *importer) ReadByte() (byte, error) {
|
|
||||||
return p.rawByte(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// byte is the bottleneck interface for reading p.data.
|
|
||||||
// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
|
|
||||||
// rawByte should only be used by low-level decoders.
|
|
||||||
func (p *importer) rawByte() byte {
|
|
||||||
b := p.data[0]
|
|
||||||
r := 1
|
|
||||||
if b == '|' {
|
|
||||||
b = p.data[1]
|
|
||||||
r = 2
|
|
||||||
switch b {
|
|
||||||
case 'S':
|
|
||||||
b = '$'
|
|
||||||
case '|':
|
|
||||||
// nothing to do
|
|
||||||
default:
|
|
||||||
errorf("unexpected escape sequence in export data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.data = p.data[r:]
|
|
||||||
p.read += r
|
|
||||||
return b
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Export format
|
|
||||||
|
|
||||||
// Tags. Must be < 0.
|
|
||||||
const (
|
|
||||||
// Objects
|
|
||||||
packageTag = -(iota + 1)
|
|
||||||
constTag
|
|
||||||
typeTag
|
|
||||||
varTag
|
|
||||||
funcTag
|
|
||||||
endTag
|
|
||||||
|
|
||||||
// Types
|
|
||||||
namedTag
|
|
||||||
arrayTag
|
|
||||||
sliceTag
|
|
||||||
dddTag
|
|
||||||
structTag
|
|
||||||
pointerTag
|
|
||||||
signatureTag
|
|
||||||
interfaceTag
|
|
||||||
mapTag
|
|
||||||
chanTag
|
|
||||||
|
|
||||||
// Values
|
|
||||||
falseTag
|
|
||||||
trueTag
|
|
||||||
int64Tag
|
|
||||||
floatTag
|
|
||||||
fractionTag // not used by gc
|
|
||||||
complexTag
|
|
||||||
stringTag
|
|
||||||
nilTag // only used by gc (appears in exported inlined function bodies)
|
|
||||||
unknownTag // not used by gc (only appears in packages with errors)
|
|
||||||
|
|
||||||
// Type aliases
|
|
||||||
aliasTag
|
|
||||||
)
|
|
||||||
|
|
||||||
var predeclOnce sync.Once
|
var predeclOnce sync.Once
|
||||||
var predecl []types.Type // initialized lazily
|
var predecl []types.Type // initialized lazily
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,18 @@
|
||||||
// Package gcimporter provides various functions for reading
|
// Package gcimporter provides various functions for reading
|
||||||
// gc-generated object files that can be used to implement the
|
// gc-generated object files that can be used to implement the
|
||||||
// Importer interface defined by the Go 1.5 standard library package.
|
// Importer interface defined by the Go 1.5 standard library package.
|
||||||
|
//
|
||||||
|
// The encoding is deterministic: if the encoder is applied twice to
|
||||||
|
// the same types.Package data structure, both encodings are equal.
|
||||||
|
// This property may be important to avoid spurious changes in
|
||||||
|
// applications such as build systems.
|
||||||
|
//
|
||||||
|
// However, the encoder is not necessarily idempotent. Importing an
|
||||||
|
// exported package may yield a types.Package that, while it
|
||||||
|
// represents the same set of Go types as the original, may differ in
|
||||||
|
// the details of its internal representation. Because of these
|
||||||
|
// differences, re-encoding the imported package may yield a
|
||||||
|
// different, but equally valid, encoding of the package.
|
||||||
package gcimporter // import "golang.org/x/tools/internal/gcimporter"
|
package gcimporter // import "golang.org/x/tools/internal/gcimporter"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -218,20 +230,17 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
|
||||||
// Or, define a new standard go/types/gcexportdata package.
|
// Or, define a new standard go/types/gcexportdata package.
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
|
|
||||||
// The indexed export format starts with an 'i'; the older
|
// Select appropriate importer.
|
||||||
// binary export format starts with a 'c', 'd', or 'v'
|
|
||||||
// (from "version"). Select appropriate importer.
|
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
switch data[0] {
|
switch data[0] {
|
||||||
case 'i':
|
case 'v', 'c', 'd': // binary, till go1.10
|
||||||
|
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
|
||||||
|
|
||||||
|
case 'i': // indexed, till go1.19
|
||||||
_, pkg, err := IImportData(fset, packages, data[1:], id)
|
_, pkg, err := IImportData(fset, packages, data[1:], id)
|
||||||
return pkg, err
|
return pkg, err
|
||||||
|
|
||||||
case 'v', 'c', 'd':
|
case 'u': // unified, from go1.20
|
||||||
_, pkg, err := BImportData(fset, packages, data, id)
|
|
||||||
return pkg, err
|
|
||||||
|
|
||||||
case 'u':
|
|
||||||
_, pkg, err := UImportData(fset, packages, data[1:size], id)
|
_, pkg, err := UImportData(fset, packages, data[1:size], id)
|
||||||
return pkg, err
|
return pkg, err
|
||||||
|
|
||||||
|
|
|
@ -22,17 +22,23 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/types/objectpath"
|
||||||
"golang.org/x/tools/internal/tokeninternal"
|
"golang.org/x/tools/internal/tokeninternal"
|
||||||
"golang.org/x/tools/internal/typeparams"
|
"golang.org/x/tools/internal/typeparams"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IExportShallow encodes "shallow" export data for the specified package.
|
// IExportShallow encodes "shallow" export data for the specified package.
|
||||||
//
|
//
|
||||||
// No promises are made about the encoding other than that it can be
|
// No promises are made about the encoding other than that it can be decoded by
|
||||||
// decoded by the same version of IIExportShallow. If you plan to save
|
// the same version of IIExportShallow. If you plan to save export data in the
|
||||||
// export data in the file system, be sure to include a cryptographic
|
// file system, be sure to include a cryptographic digest of the executable in
|
||||||
// digest of the executable in the key to avoid version skew.
|
// the key to avoid version skew.
|
||||||
func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
|
//
|
||||||
|
// If the provided reportf func is non-nil, it will be used for reporting bugs
|
||||||
|
// encountered during export.
|
||||||
|
// TODO(rfindley): remove reportf when we are confident enough in the new
|
||||||
|
// objectpath encoding.
|
||||||
|
func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) ([]byte, error) {
|
||||||
// In principle this operation can only fail if out.Write fails,
|
// In principle this operation can only fail if out.Write fails,
|
||||||
// but that's impossible for bytes.Buffer---and as a matter of
|
// but that's impossible for bytes.Buffer---and as a matter of
|
||||||
// fact iexportCommon doesn't even check for I/O errors.
|
// fact iexportCommon doesn't even check for I/O errors.
|
||||||
|
@ -44,22 +50,30 @@ func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
|
||||||
return out.Bytes(), err
|
return out.Bytes(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IImportShallow decodes "shallow" types.Package data encoded by IExportShallow
|
// IImportShallow decodes "shallow" types.Package data encoded by
|
||||||
// in the same executable. This function cannot import data from
|
// IExportShallow in the same executable. This function cannot import data from
|
||||||
// cmd/compile or gcexportdata.Write.
|
// cmd/compile or gcexportdata.Write.
|
||||||
func IImportShallow(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string, insert InsertType) (*types.Package, error) {
|
//
|
||||||
|
// The importer calls getPackages to obtain package symbols for all
|
||||||
|
// packages mentioned in the export data, including the one being
|
||||||
|
// decoded.
|
||||||
|
//
|
||||||
|
// If the provided reportf func is non-nil, it will be used for reporting bugs
|
||||||
|
// encountered during import.
|
||||||
|
// TODO(rfindley): remove reportf when we are confident enough in the new
|
||||||
|
// objectpath encoding.
|
||||||
|
func IImportShallow(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, path string, reportf ReportFunc) (*types.Package, error) {
|
||||||
const bundle = false
|
const bundle = false
|
||||||
pkgs, err := iimportCommon(fset, imports, data, bundle, path, insert)
|
const shallow = true
|
||||||
|
pkgs, err := iimportCommon(fset, getPackages, data, bundle, path, shallow, reportf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return pkgs[0], nil
|
return pkgs[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertType is the type of a function that creates a types.TypeName
|
// ReportFunc is the type of a function used to report formatted bugs.
|
||||||
// object for a named type and inserts it into the scope of the
|
type ReportFunc = func(string, ...interface{})
|
||||||
// specified Package.
|
|
||||||
type InsertType = func(pkg *types.Package, name string)
|
|
||||||
|
|
||||||
// Current bundled export format version. Increase with each format change.
|
// Current bundled export format version. Increase with each format change.
|
||||||
// 0: initial implementation
|
// 0: initial implementation
|
||||||
|
@ -313,8 +327,9 @@ type iexporter struct {
|
||||||
out *bytes.Buffer
|
out *bytes.Buffer
|
||||||
version int
|
version int
|
||||||
|
|
||||||
shallow bool // don't put types from other packages in the index
|
shallow bool // don't put types from other packages in the index
|
||||||
localpkg *types.Package // (nil in bundle mode)
|
objEncoder *objectpath.Encoder // encodes objects from other packages in shallow mode; lazily allocated
|
||||||
|
localpkg *types.Package // (nil in bundle mode)
|
||||||
|
|
||||||
// allPkgs tracks all packages that have been referenced by
|
// allPkgs tracks all packages that have been referenced by
|
||||||
// the export data, so we can ensure to include them in the
|
// the export data, so we can ensure to include them in the
|
||||||
|
@ -354,6 +369,17 @@ func (p *iexporter) trace(format string, args ...interface{}) {
|
||||||
fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...)
|
fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// objectpathEncoder returns the lazily allocated objectpath.Encoder to use
|
||||||
|
// when encoding objects in other packages during shallow export.
|
||||||
|
//
|
||||||
|
// Using a shared Encoder amortizes some of cost of objectpath search.
|
||||||
|
func (p *iexporter) objectpathEncoder() *objectpath.Encoder {
|
||||||
|
if p.objEncoder == nil {
|
||||||
|
p.objEncoder = new(objectpath.Encoder)
|
||||||
|
}
|
||||||
|
return p.objEncoder
|
||||||
|
}
|
||||||
|
|
||||||
// stringOff returns the offset of s within the string section.
|
// stringOff returns the offset of s within the string section.
|
||||||
// If not already present, it's added to the end.
|
// If not already present, it's added to the end.
|
||||||
func (p *iexporter) stringOff(s string) uint64 {
|
func (p *iexporter) stringOff(s string) uint64 {
|
||||||
|
@ -413,7 +439,6 @@ type exportWriter struct {
|
||||||
p *iexporter
|
p *iexporter
|
||||||
|
|
||||||
data intWriter
|
data intWriter
|
||||||
currPkg *types.Package
|
|
||||||
prevFile string
|
prevFile string
|
||||||
prevLine int64
|
prevLine int64
|
||||||
prevColumn int64
|
prevColumn int64
|
||||||
|
@ -436,7 +461,6 @@ func (p *iexporter) doDecl(obj types.Object) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
w := p.newWriter()
|
w := p.newWriter()
|
||||||
w.setPkg(obj.Pkg(), false)
|
|
||||||
|
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *types.Var:
|
case *types.Var:
|
||||||
|
@ -673,6 +697,9 @@ func (w *exportWriter) qualifiedType(obj *types.TypeName) {
|
||||||
w.pkg(obj.Pkg())
|
w.pkg(obj.Pkg())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(rfindley): what does 'pkg' even mean here? It would be better to pass
|
||||||
|
// it in explicitly into signatures and structs that may use it for
|
||||||
|
// constructing fields.
|
||||||
func (w *exportWriter) typ(t types.Type, pkg *types.Package) {
|
func (w *exportWriter) typ(t types.Type, pkg *types.Package) {
|
||||||
w.data.uint64(w.p.typOff(t, pkg))
|
w.data.uint64(w.p.typOff(t, pkg))
|
||||||
}
|
}
|
||||||
|
@ -764,30 +791,53 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
|
||||||
|
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
w.startType(signatureType)
|
w.startType(signatureType)
|
||||||
w.setPkg(pkg, true)
|
w.pkg(pkg)
|
||||||
w.signature(t)
|
w.signature(t)
|
||||||
|
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
w.startType(structType)
|
w.startType(structType)
|
||||||
n := t.NumFields()
|
n := t.NumFields()
|
||||||
|
// Even for struct{} we must emit some qualifying package, because that's
|
||||||
|
// what the compiler does, and thus that's what the importer expects.
|
||||||
|
fieldPkg := pkg
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
w.setPkg(t.Field(0).Pkg(), true) // qualifying package for field objects
|
fieldPkg = t.Field(0).Pkg()
|
||||||
} else {
|
|
||||||
w.setPkg(pkg, true)
|
|
||||||
}
|
}
|
||||||
|
if fieldPkg == nil {
|
||||||
|
// TODO(rfindley): improve this very hacky logic.
|
||||||
|
//
|
||||||
|
// The importer expects a package to be set for all struct types, even
|
||||||
|
// those with no fields. A better encoding might be to set NumFields
|
||||||
|
// before pkg. setPkg panics with a nil package, which may be possible
|
||||||
|
// to reach with invalid packages (and perhaps valid packages, too?), so
|
||||||
|
// (arbitrarily) set the localpkg if available.
|
||||||
|
//
|
||||||
|
// Alternatively, we may be able to simply guarantee that pkg != nil, by
|
||||||
|
// reconsidering the encoding of constant values.
|
||||||
|
if w.p.shallow {
|
||||||
|
fieldPkg = w.p.localpkg
|
||||||
|
} else {
|
||||||
|
panic(internalErrorf("no package to set for empty struct"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.pkg(fieldPkg)
|
||||||
w.uint64(uint64(n))
|
w.uint64(uint64(n))
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
f := t.Field(i)
|
f := t.Field(i)
|
||||||
|
if w.p.shallow {
|
||||||
|
w.objectPath(f)
|
||||||
|
}
|
||||||
w.pos(f.Pos())
|
w.pos(f.Pos())
|
||||||
w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg
|
w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg
|
||||||
w.typ(f.Type(), pkg)
|
w.typ(f.Type(), fieldPkg)
|
||||||
w.bool(f.Anonymous())
|
w.bool(f.Anonymous())
|
||||||
w.string(t.Tag(i)) // note (or tag)
|
w.string(t.Tag(i)) // note (or tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
w.startType(interfaceType)
|
w.startType(interfaceType)
|
||||||
w.setPkg(pkg, true)
|
w.pkg(pkg)
|
||||||
|
|
||||||
n := t.NumEmbeddeds()
|
n := t.NumEmbeddeds()
|
||||||
w.uint64(uint64(n))
|
w.uint64(uint64(n))
|
||||||
|
@ -802,10 +852,16 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
|
||||||
w.typ(ft, tPkg)
|
w.typ(ft, tPkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See comment for struct fields. In shallow mode we change the encoding
|
||||||
|
// for interface methods that are promoted from other packages.
|
||||||
|
|
||||||
n = t.NumExplicitMethods()
|
n = t.NumExplicitMethods()
|
||||||
w.uint64(uint64(n))
|
w.uint64(uint64(n))
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
m := t.ExplicitMethod(i)
|
m := t.ExplicitMethod(i)
|
||||||
|
if w.p.shallow {
|
||||||
|
w.objectPath(m)
|
||||||
|
}
|
||||||
w.pos(m.Pos())
|
w.pos(m.Pos())
|
||||||
w.string(m.Name())
|
w.string(m.Name())
|
||||||
sig, _ := m.Type().(*types.Signature)
|
sig, _ := m.Type().(*types.Signature)
|
||||||
|
@ -827,12 +883,61 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *exportWriter) setPkg(pkg *types.Package, write bool) {
|
// objectPath writes the package and objectPath to use to look up obj in a
|
||||||
if write {
|
// different package, when encoding in "shallow" mode.
|
||||||
w.pkg(pkg)
|
//
|
||||||
|
// When doing a shallow import, the importer creates only the local package,
|
||||||
|
// and requests package symbols for dependencies from the client.
|
||||||
|
// However, certain types defined in the local package may hold objects defined
|
||||||
|
// (perhaps deeply) within another package.
|
||||||
|
//
|
||||||
|
// For example, consider the following:
|
||||||
|
//
|
||||||
|
// package a
|
||||||
|
// func F() chan * map[string] struct { X int }
|
||||||
|
//
|
||||||
|
// package b
|
||||||
|
// import "a"
|
||||||
|
// var B = a.F()
|
||||||
|
//
|
||||||
|
// In this example, the type of b.B holds fields defined in package a.
|
||||||
|
// In order to have the correct canonical objects for the field defined in the
|
||||||
|
// type of B, they are encoded as objectPaths and later looked up in the
|
||||||
|
// importer. The same problem applies to interface methods.
|
||||||
|
func (w *exportWriter) objectPath(obj types.Object) {
|
||||||
|
if obj.Pkg() == nil || obj.Pkg() == w.p.localpkg {
|
||||||
|
// obj.Pkg() may be nil for the builtin error.Error.
|
||||||
|
// In this case, or if obj is declared in the local package, no need to
|
||||||
|
// encode.
|
||||||
|
w.string("")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
objectPath, err := w.p.objectpathEncoder().For(obj)
|
||||||
w.currPkg = pkg
|
if err != nil {
|
||||||
|
// Fall back to the empty string, which will cause the importer to create a
|
||||||
|
// new object, which matches earlier behavior. Creating a new object is
|
||||||
|
// sufficient for many purposes (such as type checking), but causes certain
|
||||||
|
// references algorithms to fail (golang/go#60819). However, we didn't
|
||||||
|
// notice this problem during months of gopls@v0.12.0 testing.
|
||||||
|
//
|
||||||
|
// TODO(golang/go#61674): this workaround is insufficient, as in the case
|
||||||
|
// where the field forwarded from an instantiated type that may not appear
|
||||||
|
// in the export data of the original package:
|
||||||
|
//
|
||||||
|
// // package a
|
||||||
|
// type A[P any] struct{ F P }
|
||||||
|
//
|
||||||
|
// // package b
|
||||||
|
// type B a.A[int]
|
||||||
|
//
|
||||||
|
// We need to update references algorithms not to depend on this
|
||||||
|
// de-duplication, at which point we may want to simply remove the
|
||||||
|
// workaround here.
|
||||||
|
w.string("")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.string(string(objectPath))
|
||||||
|
w.pkg(obj.Pkg())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *exportWriter) signature(sig *types.Signature) {
|
func (w *exportWriter) signature(sig *types.Signature) {
|
||||||
|
@ -913,6 +1018,17 @@ func (w *exportWriter) value(typ types.Type, v constant.Value) {
|
||||||
w.int64(int64(v.Kind()))
|
w.int64(int64(v.Kind()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.Kind() == constant.Unknown {
|
||||||
|
// golang/go#60605: treat unknown constant values as if they have invalid type
|
||||||
|
//
|
||||||
|
// This loses some fidelity over the package type-checked from source, but that
|
||||||
|
// is acceptable.
|
||||||
|
//
|
||||||
|
// TODO(rfindley): we should switch on the recorded constant kind rather
|
||||||
|
// than the constant type
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
|
switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
|
||||||
case types.IsBoolean:
|
case types.IsBoolean:
|
||||||
w.bool(constant.BoolVal(v))
|
w.bool(constant.BoolVal(v))
|
||||||
|
@ -969,6 +1085,16 @@ func constantToFloat(x constant.Value) *big.Float {
|
||||||
return &f
|
return &f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func valueToRat(x constant.Value) *big.Rat {
|
||||||
|
// Convert little-endian to big-endian.
|
||||||
|
// I can't believe this is necessary.
|
||||||
|
bytes := constant.Bytes(x)
|
||||||
|
for i := 0; i < len(bytes)/2; i++ {
|
||||||
|
bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
|
||||||
|
}
|
||||||
|
return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
// mpint exports a multi-precision integer.
|
// mpint exports a multi-precision integer.
|
||||||
//
|
//
|
||||||
// For unsigned types, small values are written out as a single
|
// For unsigned types, small values are written out as a single
|
||||||
|
@ -1178,3 +1304,19 @@ func (q *objQueue) popHead() types.Object {
|
||||||
q.head++
|
q.head++
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// internalError represents an error generated inside this package.
|
||||||
|
type internalError string
|
||||||
|
|
||||||
|
func (e internalError) Error() string { return "gcimporter: " + string(e) }
|
||||||
|
|
||||||
|
// TODO(adonovan): make this call panic, so that it's symmetric with errorf.
|
||||||
|
// Otherwise it's easy to forget to do anything with the error.
|
||||||
|
//
|
||||||
|
// TODO(adonovan): also, consider switching the names "errorf" and
|
||||||
|
// "internalErrorf" as the former is used for bugs, whose cause is
|
||||||
|
// internal inconsistency, whereas the latter is used for ordinary
|
||||||
|
// situations like bad input, whose cause is external.
|
||||||
|
func internalErrorf(format string, args ...interface{}) error {
|
||||||
|
return internalError(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/types/objectpath"
|
||||||
"golang.org/x/tools/internal/typeparams"
|
"golang.org/x/tools/internal/typeparams"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ const (
|
||||||
// If the export data version is not recognized or the format is otherwise
|
// If the export data version is not recognized or the format is otherwise
|
||||||
// compromised, an error is returned.
|
// compromised, an error is returned.
|
||||||
func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
|
func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
|
||||||
pkgs, err := iimportCommon(fset, imports, data, false, path, nil)
|
pkgs, err := iimportCommon(fset, GetPackagesFromMap(imports), data, false, path, false, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
@ -94,10 +95,49 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []
|
||||||
|
|
||||||
// IImportBundle imports a set of packages from the serialized package bundle.
|
// IImportBundle imports a set of packages from the serialized package bundle.
|
||||||
func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) {
|
func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) {
|
||||||
return iimportCommon(fset, imports, data, true, "", nil)
|
return iimportCommon(fset, GetPackagesFromMap(imports), data, true, "", false, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data []byte, bundle bool, path string, insert InsertType) (pkgs []*types.Package, err error) {
|
// A GetPackagesFunc function obtains the non-nil symbols for a set of
|
||||||
|
// packages, creating and recursively importing them as needed. An
|
||||||
|
// implementation should store each package symbol is in the Pkg
|
||||||
|
// field of the items array.
|
||||||
|
//
|
||||||
|
// Any error causes importing to fail. This can be used to quickly read
|
||||||
|
// the import manifest of an export data file without fully decoding it.
|
||||||
|
type GetPackagesFunc = func(items []GetPackagesItem) error
|
||||||
|
|
||||||
|
// A GetPackagesItem is a request from the importer for the package
|
||||||
|
// symbol of the specified name and path.
|
||||||
|
type GetPackagesItem struct {
|
||||||
|
Name, Path string
|
||||||
|
Pkg *types.Package // to be filled in by GetPackagesFunc call
|
||||||
|
|
||||||
|
// private importer state
|
||||||
|
pathOffset uint64
|
||||||
|
nameIndex map[string]uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPackagesFromMap returns a GetPackagesFunc that retrieves
|
||||||
|
// packages from the given map of package path to package.
|
||||||
|
//
|
||||||
|
// The returned function may mutate m: each requested package that is not
|
||||||
|
// found is created with types.NewPackage and inserted into m.
|
||||||
|
func GetPackagesFromMap(m map[string]*types.Package) GetPackagesFunc {
|
||||||
|
return func(items []GetPackagesItem) error {
|
||||||
|
for i, item := range items {
|
||||||
|
pkg, ok := m[item.Path]
|
||||||
|
if !ok {
|
||||||
|
pkg = types.NewPackage(item.Path, item.Name)
|
||||||
|
m[item.Path] = pkg
|
||||||
|
}
|
||||||
|
items[i].Pkg = pkg
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, bundle bool, path string, shallow bool, reportf ReportFunc) (pkgs []*types.Package, err error) {
|
||||||
const currentVersion = iexportVersionCurrent
|
const currentVersion = iexportVersionCurrent
|
||||||
version := int64(-1)
|
version := int64(-1)
|
||||||
if !debug {
|
if !debug {
|
||||||
|
@ -108,7 +148,7 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
|
||||||
} else if version > currentVersion {
|
} else if version > currentVersion {
|
||||||
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
|
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
|
err = fmt.Errorf("internal error while importing %q (%v); please report an issue", path, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -117,11 +157,8 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
|
||||||
r := &intReader{bytes.NewReader(data), path}
|
r := &intReader{bytes.NewReader(data), path}
|
||||||
|
|
||||||
if bundle {
|
if bundle {
|
||||||
bundleVersion := r.uint64()
|
if v := r.uint64(); v != bundleVersion {
|
||||||
switch bundleVersion {
|
errorf("unknown bundle format version %d", v)
|
||||||
case bundleVersion:
|
|
||||||
default:
|
|
||||||
errorf("unknown bundle format version %d", bundleVersion)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +176,7 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
|
||||||
sLen := int64(r.uint64())
|
sLen := int64(r.uint64())
|
||||||
var fLen int64
|
var fLen int64
|
||||||
var fileOffset []uint64
|
var fileOffset []uint64
|
||||||
if insert != nil {
|
if shallow {
|
||||||
// Shallow mode uses a different position encoding.
|
// Shallow mode uses a different position encoding.
|
||||||
fLen = int64(r.uint64())
|
fLen = int64(r.uint64())
|
||||||
fileOffset = make([]uint64, r.uint64())
|
fileOffset = make([]uint64, r.uint64())
|
||||||
|
@ -158,7 +195,8 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
|
||||||
p := iimporter{
|
p := iimporter{
|
||||||
version: int(version),
|
version: int(version),
|
||||||
ipath: path,
|
ipath: path,
|
||||||
insert: insert,
|
shallow: shallow,
|
||||||
|
reportf: reportf,
|
||||||
|
|
||||||
stringData: stringData,
|
stringData: stringData,
|
||||||
stringCache: make(map[uint64]string),
|
stringCache: make(map[uint64]string),
|
||||||
|
@ -185,8 +223,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
|
||||||
p.typCache[uint64(i)] = pt
|
p.typCache[uint64(i)] = pt
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgList := make([]*types.Package, r.uint64())
|
// Gather the relevant packages from the manifest.
|
||||||
for i := range pkgList {
|
items := make([]GetPackagesItem, r.uint64())
|
||||||
|
for i := range items {
|
||||||
pkgPathOff := r.uint64()
|
pkgPathOff := r.uint64()
|
||||||
pkgPath := p.stringAt(pkgPathOff)
|
pkgPath := p.stringAt(pkgPathOff)
|
||||||
pkgName := p.stringAt(r.uint64())
|
pkgName := p.stringAt(r.uint64())
|
||||||
|
@ -195,30 +234,42 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
|
||||||
if pkgPath == "" {
|
if pkgPath == "" {
|
||||||
pkgPath = path
|
pkgPath = path
|
||||||
}
|
}
|
||||||
pkg := imports[pkgPath]
|
items[i].Name = pkgName
|
||||||
if pkg == nil {
|
items[i].Path = pkgPath
|
||||||
pkg = types.NewPackage(pkgPath, pkgName)
|
items[i].pathOffset = pkgPathOff
|
||||||
imports[pkgPath] = pkg
|
|
||||||
} else if pkg.Name() != pkgName {
|
|
||||||
errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
|
|
||||||
}
|
|
||||||
if i == 0 && !bundle {
|
|
||||||
p.localpkg = pkg
|
|
||||||
}
|
|
||||||
|
|
||||||
p.pkgCache[pkgPathOff] = pkg
|
|
||||||
|
|
||||||
// Read index for package.
|
// Read index for package.
|
||||||
nameIndex := make(map[string]uint64)
|
nameIndex := make(map[string]uint64)
|
||||||
nSyms := r.uint64()
|
nSyms := r.uint64()
|
||||||
// In shallow mode we don't expect an index for other packages.
|
// In shallow mode, only the current package (i=0) has an index.
|
||||||
assert(nSyms == 0 || p.localpkg == pkg || p.insert == nil)
|
assert(!(shallow && i > 0 && nSyms != 0))
|
||||||
for ; nSyms > 0; nSyms-- {
|
for ; nSyms > 0; nSyms-- {
|
||||||
name := p.stringAt(r.uint64())
|
name := p.stringAt(r.uint64())
|
||||||
nameIndex[name] = r.uint64()
|
nameIndex[name] = r.uint64()
|
||||||
}
|
}
|
||||||
|
|
||||||
p.pkgIndex[pkg] = nameIndex
|
items[i].nameIndex = nameIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request packages all at once from the client,
|
||||||
|
// enabling a parallel implementation.
|
||||||
|
if err := getPackages(items); err != nil {
|
||||||
|
return nil, err // don't wrap this error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the results and complete the index.
|
||||||
|
pkgList := make([]*types.Package, len(items))
|
||||||
|
for i, item := range items {
|
||||||
|
pkg := item.Pkg
|
||||||
|
if pkg == nil {
|
||||||
|
errorf("internal error: getPackages returned nil package for %q", item.Path)
|
||||||
|
} else if pkg.Path() != item.Path {
|
||||||
|
errorf("internal error: getPackages returned wrong path %q, want %q", pkg.Path(), item.Path)
|
||||||
|
} else if pkg.Name() != item.Name {
|
||||||
|
errorf("internal error: getPackages returned wrong name %s for package %q, want %s", pkg.Name(), item.Path, item.Name)
|
||||||
|
}
|
||||||
|
p.pkgCache[item.pathOffset] = pkg
|
||||||
|
p.pkgIndex[pkg] = item.nameIndex
|
||||||
pkgList[i] = pkg
|
pkgList[i] = pkg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +328,13 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
|
||||||
typ.Complete()
|
typ.Complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for golang/go#61561. See the doc for instanceList for details.
|
||||||
|
for _, typ := range p.instanceList {
|
||||||
|
if iface, _ := typ.Underlying().(*types.Interface); iface != nil {
|
||||||
|
iface.Complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return pkgs, nil
|
return pkgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,8 +347,8 @@ type iimporter struct {
|
||||||
version int
|
version int
|
||||||
ipath string
|
ipath string
|
||||||
|
|
||||||
localpkg *types.Package
|
shallow bool
|
||||||
insert func(pkg *types.Package, name string) // "shallow" mode only
|
reportf ReportFunc // if non-nil, used to report bugs
|
||||||
|
|
||||||
stringData []byte
|
stringData []byte
|
||||||
stringCache map[uint64]string
|
stringCache map[uint64]string
|
||||||
|
@ -307,6 +365,12 @@ type iimporter struct {
|
||||||
fake fakeFileSet
|
fake fakeFileSet
|
||||||
interfaceList []*types.Interface
|
interfaceList []*types.Interface
|
||||||
|
|
||||||
|
// Workaround for the go/types bug golang/go#61561: instances produced during
|
||||||
|
// instantiation may contain incomplete interfaces. Here we only complete the
|
||||||
|
// underlying type of the instance, which is the most common case but doesn't
|
||||||
|
// handle parameterized interface literals defined deeper in the type.
|
||||||
|
instanceList []types.Type // instances for later completion (see golang/go#61561)
|
||||||
|
|
||||||
// Arguments for calls to SetConstraint that are deferred due to recursive types
|
// Arguments for calls to SetConstraint that are deferred due to recursive types
|
||||||
later []setConstraintArgs
|
later []setConstraintArgs
|
||||||
|
|
||||||
|
@ -338,13 +402,9 @@ func (p *iimporter) doDecl(pkg *types.Package, name string) {
|
||||||
|
|
||||||
off, ok := p.pkgIndex[pkg][name]
|
off, ok := p.pkgIndex[pkg][name]
|
||||||
if !ok {
|
if !ok {
|
||||||
// In "shallow" mode, call back to the application to
|
// In deep mode, the index should be complete. In shallow
|
||||||
// find the object and insert it into the package scope.
|
// mode, we should have already recursively loaded necessary
|
||||||
if p.insert != nil {
|
// dependencies so the above Lookup succeeds.
|
||||||
assert(pkg != p.localpkg)
|
|
||||||
p.insert(pkg, name) // "can't fail"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errorf("%v.%v not in index", pkg, name)
|
errorf("%v.%v not in index", pkg, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,7 +771,8 @@ func (r *importReader) qualifiedIdent() (*types.Package, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *importReader) pos() token.Pos {
|
func (r *importReader) pos() token.Pos {
|
||||||
if r.p.insert != nil { // shallow mode
|
if r.p.shallow {
|
||||||
|
// precise offsets are encoded only in shallow mode
|
||||||
return r.posv2()
|
return r.posv2()
|
||||||
}
|
}
|
||||||
if r.p.version >= iexportVersionPosCol {
|
if r.p.version >= iexportVersionPosCol {
|
||||||
|
@ -812,13 +873,28 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
|
||||||
fields := make([]*types.Var, r.uint64())
|
fields := make([]*types.Var, r.uint64())
|
||||||
tags := make([]string, len(fields))
|
tags := make([]string, len(fields))
|
||||||
for i := range fields {
|
for i := range fields {
|
||||||
|
var field *types.Var
|
||||||
|
if r.p.shallow {
|
||||||
|
field, _ = r.objectPathObject().(*types.Var)
|
||||||
|
}
|
||||||
|
|
||||||
fpos := r.pos()
|
fpos := r.pos()
|
||||||
fname := r.ident()
|
fname := r.ident()
|
||||||
ftyp := r.typ()
|
ftyp := r.typ()
|
||||||
emb := r.bool()
|
emb := r.bool()
|
||||||
tag := r.string()
|
tag := r.string()
|
||||||
|
|
||||||
fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
|
// Either this is not a shallow import, the field is local, or the
|
||||||
|
// encoded objectPath failed to produce an object (a bug).
|
||||||
|
//
|
||||||
|
// Even in this last, buggy case, fall back on creating a new field. As
|
||||||
|
// discussed in iexport.go, this is not correct, but mostly works and is
|
||||||
|
// preferable to failing (for now at least).
|
||||||
|
if field == nil {
|
||||||
|
field = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fields[i] = field
|
||||||
tags[i] = tag
|
tags[i] = tag
|
||||||
}
|
}
|
||||||
return types.NewStruct(fields, tags)
|
return types.NewStruct(fields, tags)
|
||||||
|
@ -834,6 +910,11 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
|
||||||
|
|
||||||
methods := make([]*types.Func, r.uint64())
|
methods := make([]*types.Func, r.uint64())
|
||||||
for i := range methods {
|
for i := range methods {
|
||||||
|
var method *types.Func
|
||||||
|
if r.p.shallow {
|
||||||
|
method, _ = r.objectPathObject().(*types.Func)
|
||||||
|
}
|
||||||
|
|
||||||
mpos := r.pos()
|
mpos := r.pos()
|
||||||
mname := r.ident()
|
mname := r.ident()
|
||||||
|
|
||||||
|
@ -843,9 +924,12 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
|
||||||
if base != nil {
|
if base != nil {
|
||||||
recv = types.NewVar(token.NoPos, r.currPkg, "", base)
|
recv = types.NewVar(token.NoPos, r.currPkg, "", base)
|
||||||
}
|
}
|
||||||
|
|
||||||
msig := r.signature(recv, nil, nil)
|
msig := r.signature(recv, nil, nil)
|
||||||
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
|
|
||||||
|
if method == nil {
|
||||||
|
method = types.NewFunc(mpos, r.currPkg, mname, msig)
|
||||||
|
}
|
||||||
|
methods[i] = method
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := newInterface(methods, embeddeds)
|
typ := newInterface(methods, embeddeds)
|
||||||
|
@ -883,6 +967,9 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
|
||||||
// we must always use the methods of the base (orig) type.
|
// we must always use the methods of the base (orig) type.
|
||||||
// TODO provide a non-nil *Environment
|
// TODO provide a non-nil *Environment
|
||||||
t, _ := typeparams.Instantiate(nil, baseType, targs, false)
|
t, _ := typeparams.Instantiate(nil, baseType, targs, false)
|
||||||
|
|
||||||
|
// Workaround for golang/go#61561. See the doc for instanceList for details.
|
||||||
|
r.p.instanceList = append(r.p.instanceList, t)
|
||||||
return t
|
return t
|
||||||
|
|
||||||
case unionType:
|
case unionType:
|
||||||
|
@ -901,6 +988,26 @@ func (r *importReader) kind() itag {
|
||||||
return itag(r.uint64())
|
return itag(r.uint64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// objectPathObject is the inverse of exportWriter.objectPath.
|
||||||
|
//
|
||||||
|
// In shallow mode, certain fields and methods may need to be looked up in an
|
||||||
|
// imported package. See the doc for exportWriter.objectPath for a full
|
||||||
|
// explanation.
|
||||||
|
func (r *importReader) objectPathObject() types.Object {
|
||||||
|
objPath := objectpath.Path(r.string())
|
||||||
|
if objPath == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pkg := r.pkg()
|
||||||
|
obj, err := objectpath.Object(pkg, objPath)
|
||||||
|
if err != nil {
|
||||||
|
if r.p.reportf != nil {
|
||||||
|
r.p.reportf("failed to find object for objectPath %q: %v", objPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature {
|
func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature {
|
||||||
params := r.paramList()
|
params := r.paramList()
|
||||||
results := r.paramList()
|
results := r.paramList()
|
||||||
|
|
|
@ -10,8 +10,10 @@
|
||||||
package gcimporter
|
package gcimporter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/tools/internal/pkgbits"
|
"golang.org/x/tools/internal/pkgbits"
|
||||||
|
@ -62,6 +64,14 @@ type typeInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
|
func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
|
||||||
|
if !debug {
|
||||||
|
defer func() {
|
||||||
|
if x := recover(); x != nil {
|
||||||
|
err = fmt.Errorf("internal error in importing %q (%v); please report an issue", path, x)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
s := string(data)
|
s := string(data)
|
||||||
s = s[:strings.LastIndex(s, "\n$$\n")]
|
s = s[:strings.LastIndex(s, "\n$$\n")]
|
||||||
input := pkgbits.NewPkgDecoder(path, s)
|
input := pkgbits.NewPkgDecoder(path, s)
|
||||||
|
@ -121,6 +131,16 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st
|
||||||
iface.Complete()
|
iface.Complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Imports() of pkg are all of the transitive packages that were loaded.
|
||||||
|
var imps []*types.Package
|
||||||
|
for _, imp := range pr.pkgs {
|
||||||
|
if imp != nil && imp != pkg {
|
||||||
|
imps = append(imps, imp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(byPath(imps))
|
||||||
|
pkg.SetImports(imps)
|
||||||
|
|
||||||
pkg.MarkComplete()
|
pkg.MarkComplete()
|
||||||
return pkg
|
return pkg
|
||||||
}
|
}
|
||||||
|
@ -260,39 +280,9 @@ func (r *reader) doPkg() *types.Package {
|
||||||
pkg := types.NewPackage(path, name)
|
pkg := types.NewPackage(path, name)
|
||||||
r.p.imports[path] = pkg
|
r.p.imports[path] = pkg
|
||||||
|
|
||||||
imports := make([]*types.Package, r.Len())
|
|
||||||
for i := range imports {
|
|
||||||
imports[i] = r.pkg()
|
|
||||||
}
|
|
||||||
pkg.SetImports(flattenImports(imports))
|
|
||||||
|
|
||||||
return pkg
|
return pkg
|
||||||
}
|
}
|
||||||
|
|
||||||
// flattenImports returns the transitive closure of all imported
|
|
||||||
// packages rooted from pkgs.
|
|
||||||
func flattenImports(pkgs []*types.Package) []*types.Package {
|
|
||||||
var res []*types.Package
|
|
||||||
seen := make(map[*types.Package]struct{})
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
if _, ok := seen[pkg]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[pkg] = struct{}{}
|
|
||||||
res = append(res, pkg)
|
|
||||||
|
|
||||||
// pkg.Imports() is already flattened.
|
|
||||||
for _, pkg := range pkg.Imports() {
|
|
||||||
if _, ok := seen[pkg]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[pkg] = struct{}{}
|
|
||||||
res = append(res, pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// @@@ Types
|
// @@@ Types
|
||||||
|
|
||||||
func (r *reader) typ() types.Type {
|
func (r *reader) typ() types.Type {
|
||||||
|
|
|
@ -8,10 +8,12 @@ package gocommand
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -22,6 +24,9 @@ import (
|
||||||
exec "golang.org/x/sys/execabs"
|
exec "golang.org/x/sys/execabs"
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event"
|
"golang.org/x/tools/internal/event"
|
||||||
|
"golang.org/x/tools/internal/event/keys"
|
||||||
|
"golang.org/x/tools/internal/event/label"
|
||||||
|
"golang.org/x/tools/internal/event/tag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An Runner will run go command invocations and serialize
|
// An Runner will run go command invocations and serialize
|
||||||
|
@ -51,9 +56,19 @@ func (runner *Runner) initialize() {
|
||||||
// 1.14: go: updating go.mod: existing contents have changed since last read
|
// 1.14: go: updating go.mod: existing contents have changed since last read
|
||||||
var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
|
var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
|
||||||
|
|
||||||
|
// verb is an event label for the go command verb.
|
||||||
|
var verb = keys.NewString("verb", "go command verb")
|
||||||
|
|
||||||
|
func invLabels(inv Invocation) []label.Label {
|
||||||
|
return []label.Label{verb.Of(inv.Verb), tag.Directory.Of(inv.WorkingDir)}
|
||||||
|
}
|
||||||
|
|
||||||
// Run is a convenience wrapper around RunRaw.
|
// Run is a convenience wrapper around RunRaw.
|
||||||
// It returns only stdout and a "friendly" error.
|
// It returns only stdout and a "friendly" error.
|
||||||
func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) {
|
func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) {
|
||||||
|
ctx, done := event.Start(ctx, "gocommand.Runner.Run", invLabels(inv)...)
|
||||||
|
defer done()
|
||||||
|
|
||||||
stdout, _, friendly, _ := runner.RunRaw(ctx, inv)
|
stdout, _, friendly, _ := runner.RunRaw(ctx, inv)
|
||||||
return stdout, friendly
|
return stdout, friendly
|
||||||
}
|
}
|
||||||
|
@ -61,6 +76,9 @@ func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, e
|
||||||
// RunPiped runs the invocation serially, always waiting for any concurrent
|
// RunPiped runs the invocation serially, always waiting for any concurrent
|
||||||
// invocations to complete first.
|
// invocations to complete first.
|
||||||
func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error {
|
func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error {
|
||||||
|
ctx, done := event.Start(ctx, "gocommand.Runner.RunPiped", invLabels(inv)...)
|
||||||
|
defer done()
|
||||||
|
|
||||||
_, err := runner.runPiped(ctx, inv, stdout, stderr)
|
_, err := runner.runPiped(ctx, inv, stdout, stderr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -68,6 +86,8 @@ func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stde
|
||||||
// RunRaw runs the invocation, serializing requests only if they fight over
|
// RunRaw runs the invocation, serializing requests only if they fight over
|
||||||
// go.mod changes.
|
// go.mod changes.
|
||||||
func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
|
func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
|
||||||
|
ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...)
|
||||||
|
defer done()
|
||||||
// Make sure the runner is always initialized.
|
// Make sure the runner is always initialized.
|
||||||
runner.initialize()
|
runner.initialize()
|
||||||
|
|
||||||
|
@ -215,6 +235,18 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
|
||||||
cmd := exec.Command("go", goArgs...)
|
cmd := exec.Command("go", goArgs...)
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
cmd.Stderr = stderr
|
cmd.Stderr = stderr
|
||||||
|
|
||||||
|
// cmd.WaitDelay was added only in go1.20 (see #50436).
|
||||||
|
if waitDelay := reflect.ValueOf(cmd).Elem().FieldByName("WaitDelay"); waitDelay.IsValid() {
|
||||||
|
// https://go.dev/issue/59541: don't wait forever copying stderr
|
||||||
|
// after the command has exited.
|
||||||
|
// After CL 484741 we copy stdout manually, so we we'll stop reading that as
|
||||||
|
// soon as ctx is done. However, we also don't want to wait around forever
|
||||||
|
// for stderr. Give a much-longer-than-reasonable delay and then assume that
|
||||||
|
// something has wedged in the kernel or runtime.
|
||||||
|
waitDelay.Set(reflect.ValueOf(30 * time.Second))
|
||||||
|
}
|
||||||
|
|
||||||
// On darwin the cwd gets resolved to the real path, which breaks anything that
|
// On darwin the cwd gets resolved to the real path, which breaks anything that
|
||||||
// expects the working directory to keep the original path, including the
|
// expects the working directory to keep the original path, including the
|
||||||
// go command when dealing with modules.
|
// go command when dealing with modules.
|
||||||
|
@ -229,6 +261,7 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
|
||||||
cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
|
cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
|
||||||
cmd.Dir = i.WorkingDir
|
cmd.Dir = i.WorkingDir
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
|
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
|
||||||
|
|
||||||
return runCmdContext(ctx, cmd)
|
return runCmdContext(ctx, cmd)
|
||||||
|
@ -242,10 +275,85 @@ var DebugHangingGoCommands = false
|
||||||
|
|
||||||
// runCmdContext is like exec.CommandContext except it sends os.Interrupt
|
// runCmdContext is like exec.CommandContext except it sends os.Interrupt
|
||||||
// before os.Kill.
|
// before os.Kill.
|
||||||
func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
|
func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
|
||||||
if err := cmd.Start(); err != nil {
|
// If cmd.Stdout is not an *os.File, the exec package will create a pipe and
|
||||||
|
// copy it to the Writer in a goroutine until the process has finished and
|
||||||
|
// either the pipe reaches EOF or command's WaitDelay expires.
|
||||||
|
//
|
||||||
|
// However, the output from 'go list' can be quite large, and we don't want to
|
||||||
|
// keep reading (and allocating buffers) if we've already decided we don't
|
||||||
|
// care about the output. We don't want to wait for the process to finish, and
|
||||||
|
// we don't wait to wait for the WaitDelay to expire either.
|
||||||
|
//
|
||||||
|
// Instead, if cmd.Stdout requires a copying goroutine we explicitly replace
|
||||||
|
// it with a pipe (which is an *os.File), which we can close in order to stop
|
||||||
|
// copying output as soon as we realize we don't care about it.
|
||||||
|
var stdoutW *os.File
|
||||||
|
if cmd.Stdout != nil {
|
||||||
|
if _, ok := cmd.Stdout.(*os.File); !ok {
|
||||||
|
var stdoutR *os.File
|
||||||
|
stdoutR, stdoutW, err = os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
prevStdout := cmd.Stdout
|
||||||
|
cmd.Stdout = stdoutW
|
||||||
|
|
||||||
|
stdoutErr := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(prevStdout, stdoutR)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("copying stdout: %w", err)
|
||||||
|
}
|
||||||
|
stdoutErr <- err
|
||||||
|
}()
|
||||||
|
defer func() {
|
||||||
|
// We started a goroutine to copy a stdout pipe.
|
||||||
|
// Wait for it to finish, or terminate it if need be.
|
||||||
|
var err2 error
|
||||||
|
select {
|
||||||
|
case err2 = <-stdoutErr:
|
||||||
|
stdoutR.Close()
|
||||||
|
case <-ctx.Done():
|
||||||
|
stdoutR.Close()
|
||||||
|
// Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close
|
||||||
|
// should cause the Read call in io.Copy to unblock and return
|
||||||
|
// immediately, but we still need to receive from stdoutErr to confirm
|
||||||
|
// that it has happened.
|
||||||
|
<-stdoutErr
|
||||||
|
err2 = ctx.Err()
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Per https://pkg.go.dev/os/exec#Cmd, “If Stdout and Stderr are the
|
||||||
|
// same writer, and have a type that can be compared with ==, at most
|
||||||
|
// one goroutine at a time will call Write.”
|
||||||
|
//
|
||||||
|
// Since we're starting a goroutine that writes to cmd.Stdout, we must
|
||||||
|
// also update cmd.Stderr so that it still holds.
|
||||||
|
func() {
|
||||||
|
defer func() { recover() }()
|
||||||
|
if cmd.Stderr == prevStdout {
|
||||||
|
cmd.Stderr = cmd.Stdout
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmd.Start()
|
||||||
|
if stdoutW != nil {
|
||||||
|
// The child process has inherited the pipe file,
|
||||||
|
// so close the copy held in this process.
|
||||||
|
stdoutW.Close()
|
||||||
|
stdoutW = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resChan := make(chan error, 1)
|
resChan := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
resChan <- cmd.Wait()
|
resChan <- cmd.Wait()
|
||||||
|
@ -253,11 +361,14 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
|
||||||
|
|
||||||
// If we're interested in debugging hanging Go commands, stop waiting after a
|
// If we're interested in debugging hanging Go commands, stop waiting after a
|
||||||
// minute and panic with interesting information.
|
// minute and panic with interesting information.
|
||||||
if DebugHangingGoCommands {
|
debug := DebugHangingGoCommands
|
||||||
|
if debug {
|
||||||
|
timer := time.NewTimer(1 * time.Minute)
|
||||||
|
defer timer.Stop()
|
||||||
select {
|
select {
|
||||||
case err := <-resChan:
|
case err := <-resChan:
|
||||||
return err
|
return err
|
||||||
case <-time.After(1 * time.Minute):
|
case <-timer.C:
|
||||||
HandleHangingGoCommand(cmd.Process)
|
HandleHangingGoCommand(cmd.Process)
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
}
|
}
|
||||||
|
@ -270,30 +381,25 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancelled. Interrupt and see if it ends voluntarily.
|
// Cancelled. Interrupt and see if it ends voluntarily.
|
||||||
cmd.Process.Signal(os.Interrupt)
|
if err := cmd.Process.Signal(os.Interrupt); err == nil {
|
||||||
select {
|
// (We used to wait only 1s but this proved
|
||||||
case err := <-resChan:
|
// fragile on loaded builder machines.)
|
||||||
return err
|
timer := time.NewTimer(5 * time.Second)
|
||||||
case <-time.After(time.Second):
|
defer timer.Stop()
|
||||||
|
select {
|
||||||
|
case err := <-resChan:
|
||||||
|
return err
|
||||||
|
case <-timer.C:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Didn't shut down in response to interrupt. Kill it hard.
|
// Didn't shut down in response to interrupt. Kill it hard.
|
||||||
// TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT
|
// TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT
|
||||||
// on certain platforms, such as unix.
|
// on certain platforms, such as unix.
|
||||||
if err := cmd.Process.Kill(); err != nil && DebugHangingGoCommands {
|
if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) && debug {
|
||||||
// Don't panic here as this reliably fails on windows with EINVAL.
|
|
||||||
log.Printf("error killing the Go command: %v", err)
|
log.Printf("error killing the Go command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See above: don't wait indefinitely if we're debugging hanging Go commands.
|
|
||||||
if DebugHangingGoCommands {
|
|
||||||
select {
|
|
||||||
case err := <-resChan:
|
|
||||||
return err
|
|
||||||
case <-time.After(10 * time.Second): // a shorter wait as resChan should return quickly following Kill
|
|
||||||
HandleHangingGoCommand(cmd.Process)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return <-resChan
|
return <-resChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,21 +23,11 @@ import (
|
||||||
func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
|
func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
|
||||||
inv.Verb = "list"
|
inv.Verb = "list"
|
||||||
inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
|
inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
|
||||||
inv.Env = append(append([]string{}, inv.Env...), "GO111MODULE=off")
|
inv.BuildFlags = nil // This is not a build command.
|
||||||
// Unset any unneeded flags, and remove them from BuildFlags, if they're
|
|
||||||
// present.
|
|
||||||
inv.ModFile = ""
|
|
||||||
inv.ModFlag = ""
|
inv.ModFlag = ""
|
||||||
var buildFlags []string
|
inv.ModFile = ""
|
||||||
for _, flag := range inv.BuildFlags {
|
inv.Env = append(inv.Env[:len(inv.Env):len(inv.Env)], "GO111MODULE=off")
|
||||||
// Flags can be prefixed by one or two dashes.
|
|
||||||
f := strings.TrimPrefix(strings.TrimPrefix(flag, "-"), "-")
|
|
||||||
if strings.HasPrefix(f, "mod=") || strings.HasPrefix(f, "modfile=") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buildFlags = append(buildFlags, flag)
|
|
||||||
}
|
|
||||||
inv.BuildFlags = buildFlags
|
|
||||||
stdoutBytes, err := r.Run(ctx, inv)
|
stdoutBytes, err := r.Run(ctx, inv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
package tokeninternal
|
package tokeninternal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -57,3 +59,93 @@ func GetLines(file *token.File) []int {
|
||||||
panic("unexpected token.File size")
|
panic("unexpected token.File size")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddExistingFiles adds the specified files to the FileSet if they
|
||||||
|
// are not already present. It panics if any pair of files in the
|
||||||
|
// resulting FileSet would overlap.
|
||||||
|
func AddExistingFiles(fset *token.FileSet, files []*token.File) {
|
||||||
|
// Punch through the FileSet encapsulation.
|
||||||
|
type tokenFileSet struct {
|
||||||
|
// This type remained essentially consistent from go1.16 to go1.21.
|
||||||
|
mutex sync.RWMutex
|
||||||
|
base int
|
||||||
|
files []*token.File
|
||||||
|
_ *token.File // changed to atomic.Pointer[token.File] in go1.19
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the size of token.FileSet changes, this will fail to compile.
|
||||||
|
const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{}))
|
||||||
|
var _ [-delta * delta]int
|
||||||
|
|
||||||
|
type uP = unsafe.Pointer
|
||||||
|
var ptr *tokenFileSet
|
||||||
|
*(*uP)(uP(&ptr)) = uP(fset)
|
||||||
|
ptr.mutex.Lock()
|
||||||
|
defer ptr.mutex.Unlock()
|
||||||
|
|
||||||
|
// Merge and sort.
|
||||||
|
newFiles := append(ptr.files, files...)
|
||||||
|
sort.Slice(newFiles, func(i, j int) bool {
|
||||||
|
return newFiles[i].Base() < newFiles[j].Base()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Reject overlapping files.
|
||||||
|
// Discard adjacent identical files.
|
||||||
|
out := newFiles[:0]
|
||||||
|
for i, file := range newFiles {
|
||||||
|
if i > 0 {
|
||||||
|
prev := newFiles[i-1]
|
||||||
|
if file == prev {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if prev.Base()+prev.Size()+1 > file.Base() {
|
||||||
|
panic(fmt.Sprintf("file %s (%d-%d) overlaps with file %s (%d-%d)",
|
||||||
|
prev.Name(), prev.Base(), prev.Base()+prev.Size(),
|
||||||
|
file.Name(), file.Base(), file.Base()+file.Size()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out = append(out, file)
|
||||||
|
}
|
||||||
|
newFiles = out
|
||||||
|
|
||||||
|
ptr.files = newFiles
|
||||||
|
|
||||||
|
// Advance FileSet.Base().
|
||||||
|
if len(newFiles) > 0 {
|
||||||
|
last := newFiles[len(newFiles)-1]
|
||||||
|
newBase := last.Base() + last.Size() + 1
|
||||||
|
if ptr.base < newBase {
|
||||||
|
ptr.base = newBase
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileSetFor returns a new FileSet containing a sequence of new Files with
|
||||||
|
// the same base, size, and line as the input files, for use in APIs that
|
||||||
|
// require a FileSet.
|
||||||
|
//
|
||||||
|
// Precondition: the input files must be non-overlapping, and sorted in order
|
||||||
|
// of their Base.
|
||||||
|
func FileSetFor(files ...*token.File) *token.FileSet {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
for _, f := range files {
|
||||||
|
f2 := fset.AddFile(f.Name(), f.Base(), f.Size())
|
||||||
|
lines := GetLines(f)
|
||||||
|
f2.SetLines(lines)
|
||||||
|
}
|
||||||
|
return fset
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneFileSet creates a new FileSet holding all files in fset. It does not
|
||||||
|
// create copies of the token.Files in fset: they are added to the resulting
|
||||||
|
// FileSet unmodified.
|
||||||
|
func CloneFileSet(fset *token.FileSet) *token.FileSet {
|
||||||
|
var files []*token.File
|
||||||
|
fset.Iterate(func(f *token.File) bool {
|
||||||
|
files = append(files, f)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
newFileSet := token.NewFileSet()
|
||||||
|
AddExistingFiles(newFileSet, files)
|
||||||
|
return newFileSet
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
package typeparams
|
package typeparams
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
@ -87,7 +88,6 @@ func IsTypeParam(t types.Type) bool {
|
||||||
func OriginMethod(fn *types.Func) *types.Func {
|
func OriginMethod(fn *types.Func) *types.Func {
|
||||||
recv := fn.Type().(*types.Signature).Recv()
|
recv := fn.Type().(*types.Signature).Recv()
|
||||||
if recv == nil {
|
if recv == nil {
|
||||||
|
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
base := recv.Type()
|
base := recv.Type()
|
||||||
|
@ -106,6 +106,31 @@ func OriginMethod(fn *types.Func) *types.Func {
|
||||||
}
|
}
|
||||||
orig := NamedTypeOrigin(named)
|
orig := NamedTypeOrigin(named)
|
||||||
gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name())
|
gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name())
|
||||||
|
|
||||||
|
// This is a fix for a gopls crash (#60628) due to a go/types bug (#60634). In:
|
||||||
|
// package p
|
||||||
|
// type T *int
|
||||||
|
// func (*T) f() {}
|
||||||
|
// LookupFieldOrMethod(T, true, p, f)=nil, but NewMethodSet(*T)={(*T).f}.
|
||||||
|
// Here we make them consistent by force.
|
||||||
|
// (The go/types bug is general, but this workaround is reached only
|
||||||
|
// for generic T thanks to the early return above.)
|
||||||
|
if gfn == nil {
|
||||||
|
mset := types.NewMethodSet(types.NewPointer(orig))
|
||||||
|
for i := 0; i < mset.Len(); i++ {
|
||||||
|
m := mset.At(i)
|
||||||
|
if m.Obj().Id() == fn.Id() {
|
||||||
|
gfn = m.Obj()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In golang/go#61196, we observe another crash, this time inexplicable.
|
||||||
|
if gfn == nil {
|
||||||
|
panic(fmt.Sprintf("missing origin method for %s.%s; named == origin: %t, named.NumMethods(): %d, origin.NumMethods(): %d", named, fn, named == orig, named.NumMethods(), orig.NumMethods()))
|
||||||
|
}
|
||||||
|
|
||||||
return gfn.(*types.Func)
|
return gfn.(*types.Func)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,13 +81,13 @@ func CoreType(T types.Type) types.Type {
|
||||||
// restrictions may be arbitrarily complex. For example, consider the
|
// restrictions may be arbitrarily complex. For example, consider the
|
||||||
// following:
|
// following:
|
||||||
//
|
//
|
||||||
// type A interface{ ~string|~[]byte }
|
// type A interface{ ~string|~[]byte }
|
||||||
//
|
//
|
||||||
// type B interface{ int|string }
|
// type B interface{ int|string }
|
||||||
//
|
//
|
||||||
// type C interface { ~string|~int }
|
// type C interface { ~string|~int }
|
||||||
//
|
//
|
||||||
// type T[P interface{ A|B; C }] int
|
// type T[P interface{ A|B; C }] int
|
||||||
//
|
//
|
||||||
// In this example, the structural type restriction of P is ~string|int: A|B
|
// In this example, the structural type restriction of P is ~string|int: A|B
|
||||||
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
|
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
|
||||||
|
|
|
@ -30,7 +30,7 @@ func (xl termlist) String() string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
for i, x := range xl {
|
for i, x := range xl {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
buf.WriteString(" ∪ ")
|
buf.WriteString(" | ")
|
||||||
}
|
}
|
||||||
buf.WriteString(x.String())
|
buf.WriteString(x.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ func NamedTypeArgs(*types.Named) *TypeList {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NamedTypeOrigin is the identity method at this Go version.
|
// NamedTypeOrigin is the identity method at this Go version.
|
||||||
func NamedTypeOrigin(named *types.Named) types.Type {
|
func NamedTypeOrigin(named *types.Named) *types.Named {
|
||||||
return named
|
return named
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ func NamedTypeArgs(named *types.Named) *TypeList {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NamedTypeOrigin returns named.Orig().
|
// NamedTypeOrigin returns named.Orig().
|
||||||
func NamedTypeOrigin(named *types.Named) types.Type {
|
func NamedTypeOrigin(named *types.Named) *types.Named {
|
||||||
return named.Origin()
|
return named.Origin()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,10 @@ import "go/types"
|
||||||
|
|
||||||
// A term describes elementary type sets:
|
// A term describes elementary type sets:
|
||||||
//
|
//
|
||||||
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
|
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
|
||||||
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
|
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
|
||||||
// T: &term{false, T} == {T} // set of type T
|
// T: &term{false, T} == {T} // set of type T
|
||||||
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
|
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
|
||||||
//
|
|
||||||
type term struct {
|
type term struct {
|
||||||
tilde bool // valid if typ != nil
|
tilde bool // valid if typ != nil
|
||||||
typ types.Type
|
typ types.Type
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2023 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 typesinternal
|
||||||
|
|
||||||
|
import "go/types"
|
||||||
|
|
||||||
|
// This file contains back doors that allow gopls to avoid method sorting when
|
||||||
|
// using the objectpath package.
|
||||||
|
//
|
||||||
|
// This is performance-critical in certain repositories, but changing the
|
||||||
|
// behavior of the objectpath package is still being discussed in
|
||||||
|
// golang/go#61443. If we decide to remove the sorting in objectpath we can
|
||||||
|
// simply delete these back doors. Otherwise, we should add a new API to
|
||||||
|
// objectpath that allows controlling the sorting.
|
||||||
|
|
||||||
|
// SkipEncoderMethodSorting marks enc (which must be an *objectpath.Encoder) as
|
||||||
|
// not requiring sorted methods.
|
||||||
|
var SkipEncoderMethodSorting func(enc interface{})
|
||||||
|
|
||||||
|
// ObjectpathObject is like objectpath.Object, but allows suppressing method
|
||||||
|
// sorting.
|
||||||
|
var ObjectpathObject func(pkg *types.Package, p string, skipMethodSorting bool) (types.Object, error)
|
|
@ -384,7 +384,7 @@ github.com/mattn/go-isatty
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/microcosm-cc/bluemonday
|
github.com/microcosm-cc/bluemonday
|
||||||
github.com/microcosm-cc/bluemonday/css
|
github.com/microcosm-cc/bluemonday/css
|
||||||
# github.com/miekg/dns v1.1.55
|
# github.com/miekg/dns v1.1.56
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/miekg/dns
|
github.com/miekg/dns
|
||||||
# github.com/minio/md5-simd v1.1.2
|
# github.com/minio/md5-simd v1.1.2
|
||||||
|
@ -849,7 +849,7 @@ golang.org/x/image/tiff/lzw
|
||||||
golang.org/x/image/vp8
|
golang.org/x/image/vp8
|
||||||
golang.org/x/image/vp8l
|
golang.org/x/image/vp8l
|
||||||
golang.org/x/image/webp
|
golang.org/x/image/webp
|
||||||
# golang.org/x/mod v0.10.0
|
# golang.org/x/mod v0.12.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
golang.org/x/mod/semver
|
golang.org/x/mod/semver
|
||||||
# golang.org/x/net v0.15.0
|
# golang.org/x/net v0.15.0
|
||||||
|
@ -897,15 +897,17 @@ golang.org/x/text/transform
|
||||||
golang.org/x/text/unicode/bidi
|
golang.org/x/text/unicode/bidi
|
||||||
golang.org/x/text/unicode/norm
|
golang.org/x/text/unicode/norm
|
||||||
golang.org/x/text/width
|
golang.org/x/text/width
|
||||||
# golang.org/x/tools v0.6.0
|
# golang.org/x/tools v0.13.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
golang.org/x/tools/go/gcexportdata
|
golang.org/x/tools/go/gcexportdata
|
||||||
golang.org/x/tools/go/internal/packagesdriver
|
golang.org/x/tools/go/internal/packagesdriver
|
||||||
golang.org/x/tools/go/packages
|
golang.org/x/tools/go/packages
|
||||||
|
golang.org/x/tools/go/types/objectpath
|
||||||
golang.org/x/tools/internal/event
|
golang.org/x/tools/internal/event
|
||||||
golang.org/x/tools/internal/event/core
|
golang.org/x/tools/internal/event/core
|
||||||
golang.org/x/tools/internal/event/keys
|
golang.org/x/tools/internal/event/keys
|
||||||
golang.org/x/tools/internal/event/label
|
golang.org/x/tools/internal/event/label
|
||||||
|
golang.org/x/tools/internal/event/tag
|
||||||
golang.org/x/tools/internal/gcimporter
|
golang.org/x/tools/internal/gcimporter
|
||||||
golang.org/x/tools/internal/gocommand
|
golang.org/x/tools/internal/gocommand
|
||||||
golang.org/x/tools/internal/packagesinternal
|
golang.org/x/tools/internal/packagesinternal
|
||||||
|
|
Loading…
Reference in New Issue