mirror of https://github.com/docker/cli.git
vendor: github.com/Microsoft/go-winio v0.6.2
- Fix compatibility with go1.22 - fileinfo: internally fix FileBasicInfo memory alignment (fixes compatibility with go1.22) - Switch from syscall to golang.org/x/sys/windows - Remove golang.org/x/mod as dependency - Remove golang.org/x/tools as dependency full diff: https://github.com/Microsoft/go-winio/compare/v0.6.1...v0.6.2 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
f53a2ae443
commit
12aaeae21b
|
@ -55,7 +55,7 @@ require (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/Microsoft/hcsshim v0.11.4 // indirect
|
github.com/Microsoft/hcsshim v0.11.4 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
|
@ -90,10 +90,8 @@ require (
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||||
golang.org/x/crypto v0.21.0 // indirect
|
golang.org/x/crypto v0.21.0 // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
|
||||||
golang.org/x/net v0.23.0 // indirect
|
golang.org/x/net v0.23.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.16.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||||
google.golang.org/grpc v1.60.1 // indirect
|
google.golang.org/grpc v1.60.1 // indirect
|
||||||
|
|
|
@ -6,8 +6,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
|
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
|
||||||
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
|
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ=
|
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ=
|
||||||
|
@ -329,8 +329,6 @@ golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
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=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
@ -377,8 +375,6 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
|
|
||||||
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
|
||||||
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=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
run:
|
|
||||||
skip-dirs:
|
|
||||||
- pkg/etw/sample
|
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
# style
|
# style
|
||||||
|
@ -20,9 +16,13 @@ linters:
|
||||||
- gofmt # files are gofmt'ed
|
- gofmt # files are gofmt'ed
|
||||||
- gosec # security
|
- gosec # security
|
||||||
- nilerr # returns nil even with non-nil error
|
- nilerr # returns nil even with non-nil error
|
||||||
|
- thelper # test helpers without t.Helper()
|
||||||
- unparam # unused function params
|
- unparam # unused function params
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
exclude-dirs:
|
||||||
|
- pkg/etw/sample
|
||||||
|
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
# err is very often shadowed in nested scopes
|
# err is very often shadowed in nested scopes
|
||||||
- linters:
|
- linters:
|
||||||
|
@ -69,9 +69,7 @@ linters-settings:
|
||||||
# struct order is often for Win32 compat
|
# struct order is often for Win32 compat
|
||||||
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
||||||
- fieldalignment
|
- fieldalignment
|
||||||
check-shadowing: true
|
|
||||||
nolintlint:
|
nolintlint:
|
||||||
allow-leading-space: false
|
|
||||||
require-explanation: true
|
require-explanation: true
|
||||||
require-specific: true
|
require-specific: true
|
||||||
revive:
|
revive:
|
||||||
|
|
|
@ -10,14 +10,14 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/internal/fs"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BackupData = uint32(iota + 1)
|
BackupData = uint32(iota + 1)
|
||||||
|
@ -104,7 +104,7 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hdr.Name = syscall.UTF16ToString(name)
|
hdr.Name = windows.UTF16ToString(name)
|
||||||
}
|
}
|
||||||
if wsi.StreamID == BackupSparseBlock {
|
if wsi.StreamID == BackupSparseBlock {
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||||
|
@ -205,7 +205,7 @@ func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
||||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
var bytesRead uint32
|
var bytesRead uint32
|
||||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
// the underlying file.
|
// the underlying file.
|
||||||
func (r *BackupFileReader) Close() error {
|
func (r *BackupFileReader) Close() error {
|
||||||
if r.ctx != 0 {
|
if r.ctx != 0 {
|
||||||
_ = backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
_ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||||
runtime.KeepAlive(r.f)
|
runtime.KeepAlive(r.f)
|
||||||
r.ctx = 0
|
r.ctx = 0
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
||||||
// Write restores a portion of the file using the provided backup stream.
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
var bytesWritten uint32
|
var bytesWritten uint32
|
||||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
// close the underlying file.
|
// close the underlying file.
|
||||||
func (w *BackupFileWriter) Close() error {
|
func (w *BackupFileWriter) Close() error {
|
||||||
if w.ctx != 0 {
|
if w.ctx != 0 {
|
||||||
_ = backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
_ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||||
runtime.KeepAlive(w.f)
|
runtime.KeepAlive(w.f)
|
||||||
w.ctx = 0
|
w.ctx = 0
|
||||||
}
|
}
|
||||||
|
@ -271,17 +271,14 @@ func (w *BackupFileWriter) Close() error {
|
||||||
//
|
//
|
||||||
// If the file opened was a directory, it cannot be used with Readdir().
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||||
winPath, err := syscall.UTF16FromString(path)
|
h, err := fs.CreateFile(path,
|
||||||
if err != nil {
|
fs.AccessMask(access),
|
||||||
return nil, err
|
fs.FileShareMode(share),
|
||||||
}
|
|
||||||
h, err := syscall.CreateFile(&winPath[0],
|
|
||||||
access,
|
|
||||||
share,
|
|
||||||
nil,
|
nil,
|
||||||
createmode,
|
fs.FileCreationDisposition(createmode),
|
||||||
syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT,
|
fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
0)
|
0,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = &os.PathError{Op: "open", Path: path, Err: err}
|
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -15,26 +15,11 @@ import (
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
//sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx
|
||||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
//sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort
|
||||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
//sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
||||||
|
|
||||||
type atomicBool int32
|
|
||||||
|
|
||||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
|
||||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
|
||||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
|
||||||
|
|
||||||
//revive:disable-next-line:predeclared Keep "new" to maintain consistency with "atomic" pkg
|
|
||||||
func (b *atomicBool) swap(new bool) bool {
|
|
||||||
var newInt int32
|
|
||||||
if new {
|
|
||||||
newInt = 1
|
|
||||||
}
|
|
||||||
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrFileClosed = errors.New("file has already been closed")
|
ErrFileClosed = errors.New("file has already been closed")
|
||||||
|
@ -50,7 +35,7 @@ func (*timeoutError) Temporary() bool { return true }
|
||||||
type timeoutChan chan struct{}
|
type timeoutChan chan struct{}
|
||||||
|
|
||||||
var ioInitOnce sync.Once
|
var ioInitOnce sync.Once
|
||||||
var ioCompletionPort syscall.Handle
|
var ioCompletionPort windows.Handle
|
||||||
|
|
||||||
// ioResult contains the result of an asynchronous IO operation.
|
// ioResult contains the result of an asynchronous IO operation.
|
||||||
type ioResult struct {
|
type ioResult struct {
|
||||||
|
@ -60,12 +45,12 @@ type ioResult struct {
|
||||||
|
|
||||||
// ioOperation represents an outstanding asynchronous Win32 IO.
|
// ioOperation represents an outstanding asynchronous Win32 IO.
|
||||||
type ioOperation struct {
|
type ioOperation struct {
|
||||||
o syscall.Overlapped
|
o windows.Overlapped
|
||||||
ch chan ioResult
|
ch chan ioResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func initIO() {
|
func initIO() {
|
||||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
h, err := createIoCompletionPort(windows.InvalidHandle, 0, 0, 0xffffffff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -76,10 +61,10 @@ func initIO() {
|
||||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
type win32File struct {
|
type win32File struct {
|
||||||
handle syscall.Handle
|
handle windows.Handle
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
wgLock sync.RWMutex
|
wgLock sync.RWMutex
|
||||||
closing atomicBool
|
closing atomic.Bool
|
||||||
socket bool
|
socket bool
|
||||||
readDeadline deadlineHandler
|
readDeadline deadlineHandler
|
||||||
writeDeadline deadlineHandler
|
writeDeadline deadlineHandler
|
||||||
|
@ -90,11 +75,11 @@ type deadlineHandler struct {
|
||||||
channel timeoutChan
|
channel timeoutChan
|
||||||
channelLock sync.RWMutex
|
channelLock sync.RWMutex
|
||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
timedout atomicBool
|
timedout atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeWin32File makes a new win32File from an existing file handle.
|
// makeWin32File makes a new win32File from an existing file handle.
|
||||||
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
func makeWin32File(h windows.Handle) (*win32File, error) {
|
||||||
f := &win32File{handle: h}
|
f := &win32File{handle: h}
|
||||||
ioInitOnce.Do(initIO)
|
ioInitOnce.Do(initIO)
|
||||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
|
@ -110,7 +95,12 @@ func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use NewOpenFile instead.
|
||||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
return NewOpenFile(windows.Handle(h))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOpenFile(h windows.Handle) (io.ReadWriteCloser, error) {
|
||||||
// If we return the result of makeWin32File directly, it can result in an
|
// If we return the result of makeWin32File directly, it can result in an
|
||||||
// interface-wrapped nil, rather than a nil interface value.
|
// interface-wrapped nil, rather than a nil interface value.
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
|
@ -124,13 +114,13 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
func (f *win32File) closeHandle() {
|
func (f *win32File) closeHandle() {
|
||||||
f.wgLock.Lock()
|
f.wgLock.Lock()
|
||||||
// Atomically set that we are closing, releasing the resources only once.
|
// Atomically set that we are closing, releasing the resources only once.
|
||||||
if !f.closing.swap(true) {
|
if !f.closing.Swap(true) {
|
||||||
f.wgLock.Unlock()
|
f.wgLock.Unlock()
|
||||||
// cancel all IO and wait for it to complete
|
// cancel all IO and wait for it to complete
|
||||||
_ = cancelIoEx(f.handle, nil)
|
_ = cancelIoEx(f.handle, nil)
|
||||||
f.wg.Wait()
|
f.wg.Wait()
|
||||||
// at this point, no new IO can start
|
// at this point, no new IO can start
|
||||||
syscall.Close(f.handle)
|
windows.Close(f.handle)
|
||||||
f.handle = 0
|
f.handle = 0
|
||||||
} else {
|
} else {
|
||||||
f.wgLock.Unlock()
|
f.wgLock.Unlock()
|
||||||
|
@ -145,14 +135,14 @@ func (f *win32File) Close() error {
|
||||||
|
|
||||||
// IsClosed checks if the file has been closed.
|
// IsClosed checks if the file has been closed.
|
||||||
func (f *win32File) IsClosed() bool {
|
func (f *win32File) IsClosed() bool {
|
||||||
return f.closing.isSet()
|
return f.closing.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareIO prepares for a new IO operation.
|
// prepareIO prepares for a new IO operation.
|
||||||
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||||
func (f *win32File) prepareIO() (*ioOperation, error) {
|
func (f *win32File) prepareIO() (*ioOperation, error) {
|
||||||
f.wgLock.RLock()
|
f.wgLock.RLock()
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
f.wgLock.RUnlock()
|
f.wgLock.RUnlock()
|
||||||
return nil, ErrFileClosed
|
return nil, ErrFileClosed
|
||||||
}
|
}
|
||||||
|
@ -164,12 +154,12 @@ func (f *win32File) prepareIO() (*ioOperation, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ioCompletionProcessor processes completed async IOs forever.
|
// ioCompletionProcessor processes completed async IOs forever.
|
||||||
func ioCompletionProcessor(h syscall.Handle) {
|
func ioCompletionProcessor(h windows.Handle) {
|
||||||
for {
|
for {
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
var key uintptr
|
var key uintptr
|
||||||
var op *ioOperation
|
var op *ioOperation
|
||||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, windows.INFINITE)
|
||||||
if op == nil {
|
if op == nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -182,11 +172,11 @@ func ioCompletionProcessor(h syscall.Handle) {
|
||||||
// asyncIO processes the return value from ReadFile or WriteFile, blocking until
|
// asyncIO processes the return value from ReadFile or WriteFile, blocking until
|
||||||
// the operation has actually completed.
|
// the operation has actually completed.
|
||||||
func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||||
if err != syscall.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
|
if err != windows.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
|
||||||
return int(bytes), err
|
return int(bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
_ = cancelIoEx(f.handle, &c.o)
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,8 +191,8 @@ func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
||||||
select {
|
select {
|
||||||
case r = <-c.ch:
|
case r = <-c.ch:
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
err = ErrFileClosed
|
err = ErrFileClosed
|
||||||
}
|
}
|
||||||
} else if err != nil && f.socket {
|
} else if err != nil && f.socket {
|
||||||
|
@ -214,7 +204,7 @@ func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
||||||
_ = cancelIoEx(f.handle, &c.o)
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
r = <-c.ch
|
r = <-c.ch
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
err = ErrTimeout
|
err = ErrTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,23 +225,22 @@ func (f *win32File) Read(b []byte) (int, error) {
|
||||||
}
|
}
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if f.readDeadline.timedout.isSet() {
|
if f.readDeadline.timedout.Load() {
|
||||||
return 0, ErrTimeout
|
return 0, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
err = windows.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
|
n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
|
||||||
runtime.KeepAlive(b)
|
runtime.KeepAlive(b)
|
||||||
|
|
||||||
// Handle EOF conditions.
|
// Handle EOF conditions.
|
||||||
if err == nil && n == 0 && len(b) != 0 {
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
} else if err == syscall.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
|
} else if err == windows.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
} else {
|
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes to a file handle.
|
// Write writes to a file handle.
|
||||||
|
@ -262,12 +251,12 @@ func (f *win32File) Write(b []byte) (int, error) {
|
||||||
}
|
}
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if f.writeDeadline.timedout.isSet() {
|
if f.writeDeadline.timedout.Load() {
|
||||||
return 0, ErrTimeout
|
return 0, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
err = windows.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
|
n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
|
||||||
runtime.KeepAlive(b)
|
runtime.KeepAlive(b)
|
||||||
return n, err
|
return n, err
|
||||||
|
@ -282,7 +271,7 @@ func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32File) Flush() error {
|
func (f *win32File) Flush() error {
|
||||||
return syscall.FlushFileBuffers(f.handle)
|
return windows.FlushFileBuffers(f.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32File) Fd() uintptr {
|
func (f *win32File) Fd() uintptr {
|
||||||
|
@ -299,7 +288,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
|
||||||
}
|
}
|
||||||
d.timer = nil
|
d.timer = nil
|
||||||
}
|
}
|
||||||
d.timedout.setFalse()
|
d.timedout.Store(false)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-d.channel:
|
case <-d.channel:
|
||||||
|
@ -314,7 +303,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
timeoutIO := func() {
|
timeoutIO := func() {
|
||||||
d.timedout.setTrue()
|
d.timedout.Store(true)
|
||||||
close(d.channel)
|
close(d.channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,18 @@ type FileBasicInfo struct {
|
||||||
_ uint32 // padding
|
_ uint32 // padding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alignedFileBasicInfo is a FileBasicInfo, but aligned to uint64 by containing
|
||||||
|
// uint64 rather than windows.Filetime. Filetime contains two uint32s. uint64
|
||||||
|
// alignment is necessary to pass this as FILE_BASIC_INFO.
|
||||||
|
type alignedFileBasicInfo struct {
|
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime uint64
|
||||||
|
FileAttributes uint32
|
||||||
|
_ uint32 // padding
|
||||||
|
}
|
||||||
|
|
||||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
bi := &FileBasicInfo{}
|
bi := &alignedFileBasicInfo{}
|
||||||
if err := windows.GetFileInformationByHandleEx(
|
if err := windows.GetFileInformationByHandleEx(
|
||||||
windows.Handle(f.Fd()),
|
windows.Handle(f.Fd()),
|
||||||
windows.FileBasicInfo,
|
windows.FileBasicInfo,
|
||||||
|
@ -30,16 +39,21 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(f)
|
runtime.KeepAlive(f)
|
||||||
return bi, nil
|
// Reinterpret the alignedFileBasicInfo as a FileBasicInfo so it matches the
|
||||||
|
// public API of this module. The data may be unnecessarily aligned.
|
||||||
|
return (*FileBasicInfo)(unsafe.Pointer(bi)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFileBasicInfo sets times and attributes for a file.
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||||
|
// Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is
|
||||||
|
// suitable to pass to GetFileInformationByHandleEx.
|
||||||
|
biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi))
|
||||||
if err := windows.SetFileInformationByHandle(
|
if err := windows.SetFileInformationByHandle(
|
||||||
windows.Handle(f.Fd()),
|
windows.Handle(f.Fd()),
|
||||||
windows.FileBasicInfo,
|
windows.FileBasicInfo,
|
||||||
(*byte)(unsafe.Pointer(bi)),
|
(*byte)(unsafe.Pointer(&biAligned)),
|
||||||
uint32(unsafe.Sizeof(*bi)),
|
uint32(unsafe.Sizeof(biAligned)),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
@ -181,13 +180,13 @@ type HvsockConn struct {
|
||||||
var _ net.Conn = &HvsockConn{}
|
var _ net.Conn = &HvsockConn{}
|
||||||
|
|
||||||
func newHVSocket() (*win32File, error) {
|
func newHVSocket() (*win32File, error) {
|
||||||
fd, err := syscall.Socket(afHVSock, syscall.SOCK_STREAM, 1)
|
fd, err := windows.Socket(afHVSock, windows.SOCK_STREAM, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, os.NewSyscallError("socket", err)
|
return nil, os.NewSyscallError("socket", err)
|
||||||
}
|
}
|
||||||
f, err := makeWin32File(fd)
|
f, err := makeWin32File(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(fd)
|
windows.Close(fd)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f.socket = true
|
f.socket = true
|
||||||
|
@ -197,16 +196,24 @@ func newHVSocket() (*win32File, error) {
|
||||||
// ListenHvsock listens for connections on the specified hvsock address.
|
// ListenHvsock listens for connections on the specified hvsock address.
|
||||||
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
||||||
l := &HvsockListener{addr: *addr}
|
l := &HvsockListener{addr: *addr}
|
||||||
sock, err := newHVSocket()
|
|
||||||
|
var sock *win32File
|
||||||
|
sock, err = newHVSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", err)
|
return nil, l.opErr("listen", err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = sock.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
sa := addr.raw()
|
sa := addr.raw()
|
||||||
err = socket.Bind(windows.Handle(sock.handle), &sa)
|
err = socket.Bind(sock.handle, &sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
||||||
}
|
}
|
||||||
err = syscall.Listen(sock.handle, 16)
|
err = windows.Listen(sock.handle, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
||||||
}
|
}
|
||||||
|
@ -246,7 +253,7 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
||||||
var addrbuf [addrlen * 2]byte
|
var addrbuf [addrlen * 2]byte
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
|
err = windows.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
|
||||||
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
||||||
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
||||||
}
|
}
|
||||||
|
@ -263,7 +270,7 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
||||||
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
||||||
|
|
||||||
// initialize the accepted socket and update its properties with those of the listening socket
|
// initialize the accepted socket and update its properties with those of the listening socket
|
||||||
if err = windows.Setsockopt(windows.Handle(sock.handle),
|
if err = windows.Setsockopt(sock.handle,
|
||||||
windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
|
windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
|
||||||
(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
|
(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
|
||||||
return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
|
return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
|
||||||
|
@ -334,7 +341,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
||||||
}()
|
}()
|
||||||
|
|
||||||
sa := addr.raw()
|
sa := addr.raw()
|
||||||
err = socket.Bind(windows.Handle(sock.handle), &sa)
|
err = socket.Bind(sock.handle, &sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, conn.opErr(op, os.NewSyscallError("bind", err))
|
return nil, conn.opErr(op, os.NewSyscallError("bind", err))
|
||||||
}
|
}
|
||||||
|
@ -347,7 +354,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
for i := uint(0); i <= d.Retries; i++ {
|
for i := uint(0); i <= d.Retries; i++ {
|
||||||
err = socket.ConnectEx(
|
err = socket.ConnectEx(
|
||||||
windows.Handle(sock.handle),
|
sock.handle,
|
||||||
&sa,
|
&sa,
|
||||||
nil, // sendBuf
|
nil, // sendBuf
|
||||||
0, // sendDataLen
|
0, // sendDataLen
|
||||||
|
@ -367,7 +374,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
||||||
|
|
||||||
// update the connection properties, so shutdown can be used
|
// update the connection properties, so shutdown can be used
|
||||||
if err = windows.Setsockopt(
|
if err = windows.Setsockopt(
|
||||||
windows.Handle(sock.handle),
|
sock.handle,
|
||||||
windows.SOL_SOCKET,
|
windows.SOL_SOCKET,
|
||||||
windows.SO_UPDATE_CONNECT_CONTEXT,
|
windows.SO_UPDATE_CONNECT_CONTEXT,
|
||||||
nil, // optvalue
|
nil, // optvalue
|
||||||
|
@ -378,7 +385,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
||||||
|
|
||||||
// get the local name
|
// get the local name
|
||||||
var sal rawHvsockAddr
|
var sal rawHvsockAddr
|
||||||
err = socket.GetSockName(windows.Handle(sock.handle), &sal)
|
err = socket.GetSockName(sock.handle, &sal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
|
return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
|
||||||
}
|
}
|
||||||
|
@ -421,7 +428,7 @@ func (d *HvsockDialer) redialWait(ctx context.Context) (err error) {
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// assumes error is a plain, unwrapped syscall.Errno provided by direct syscall.
|
// assumes error is a plain, unwrapped windows.Errno provided by direct syscall.
|
||||||
func canRedial(err error) bool {
|
func canRedial(err error) bool {
|
||||||
//nolint:errorlint // guaranteed to be an Errno
|
//nolint:errorlint // guaranteed to be an Errno
|
||||||
switch err {
|
switch err {
|
||||||
|
@ -447,9 +454,9 @@ func (conn *HvsockConn) Read(b []byte) (int, error) {
|
||||||
return 0, conn.opErr("read", err)
|
return 0, conn.opErr("read", err)
|
||||||
}
|
}
|
||||||
defer conn.sock.wg.Done()
|
defer conn.sock.wg.Done()
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
var flags, bytes uint32
|
var flags, bytes uint32
|
||||||
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
err = windows.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
||||||
n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
|
n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var eno windows.Errno
|
var eno windows.Errno
|
||||||
|
@ -482,9 +489,9 @@ func (conn *HvsockConn) write(b []byte) (int, error) {
|
||||||
return 0, conn.opErr("write", err)
|
return 0, conn.opErr("write", err)
|
||||||
}
|
}
|
||||||
defer conn.sock.wg.Done()
|
defer conn.sock.wg.Done()
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
err = windows.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
||||||
n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
|
n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var eno windows.Errno
|
var eno windows.Errno
|
||||||
|
@ -511,7 +518,7 @@ func (conn *HvsockConn) shutdown(how int) error {
|
||||||
return socket.ErrSocketClosed
|
return socket.ErrSocketClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
err := syscall.Shutdown(conn.sock.handle, how)
|
err := windows.Shutdown(conn.sock.handle, how)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the connection was closed, shutdowns fail with "not connected"
|
// If the connection was closed, shutdowns fail with "not connected"
|
||||||
if errors.Is(err, windows.WSAENOTCONN) ||
|
if errors.Is(err, windows.WSAENOTCONN) ||
|
||||||
|
@ -525,7 +532,7 @@ func (conn *HvsockConn) shutdown(how int) error {
|
||||||
|
|
||||||
// CloseRead shuts down the read end of the socket, preventing future read operations.
|
// CloseRead shuts down the read end of the socket, preventing future read operations.
|
||||||
func (conn *HvsockConn) CloseRead() error {
|
func (conn *HvsockConn) CloseRead() error {
|
||||||
err := conn.shutdown(syscall.SHUT_RD)
|
err := conn.shutdown(windows.SHUT_RD)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conn.opErr("closeread", err)
|
return conn.opErr("closeread", err)
|
||||||
}
|
}
|
||||||
|
@ -535,7 +542,7 @@ func (conn *HvsockConn) CloseRead() error {
|
||||||
// CloseWrite shuts down the write end of the socket, preventing future write operations and
|
// CloseWrite shuts down the write end of the socket, preventing future write operations and
|
||||||
// notifying the other endpoint that no more data will be written.
|
// notifying the other endpoint that no more data will be written.
|
||||||
func (conn *HvsockConn) CloseWrite() error {
|
func (conn *HvsockConn) CloseWrite() error {
|
||||||
err := conn.shutdown(syscall.SHUT_WR)
|
err := conn.shutdown(windows.SHUT_WR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conn.opErr("closewrite", err)
|
return conn.opErr("closewrite", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,14 @@ import (
|
||||||
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
|
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
||||||
//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
|
//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
|
||||||
|
|
||||||
const NullHandle windows.Handle = 0
|
const NullHandle windows.Handle = 0
|
||||||
|
|
||||||
// AccessMask defines standard, specific, and generic rights.
|
// AccessMask defines standard, specific, and generic rights.
|
||||||
//
|
//
|
||||||
|
// Used with CreateFile and NtCreateFile (and co.).
|
||||||
|
//
|
||||||
// Bitmask:
|
// Bitmask:
|
||||||
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
||||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||||
|
@ -47,6 +49,12 @@ const (
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
|
||||||
FILE_ANY_ACCESS AccessMask = 0
|
FILE_ANY_ACCESS AccessMask = 0
|
||||||
|
|
||||||
|
GENERIC_READ AccessMask = 0x8000_0000
|
||||||
|
GENERIC_WRITE AccessMask = 0x4000_0000
|
||||||
|
GENERIC_EXECUTE AccessMask = 0x2000_0000
|
||||||
|
GENERIC_ALL AccessMask = 0x1000_0000
|
||||||
|
ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000
|
||||||
|
|
||||||
// Specific Object Access
|
// Specific Object Access
|
||||||
// from ntioapi.h
|
// from ntioapi.h
|
||||||
|
|
||||||
|
@ -124,14 +132,32 @@ const (
|
||||||
TRUNCATE_EXISTING FileCreationDisposition = 0x05
|
TRUNCATE_EXISTING FileCreationDisposition = 0x05
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create disposition values for NtCreate*
|
||||||
|
type NTFileCreationDisposition uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// From ntioapi.h
|
||||||
|
|
||||||
|
FILE_SUPERSEDE NTFileCreationDisposition = 0x00
|
||||||
|
FILE_OPEN NTFileCreationDisposition = 0x01
|
||||||
|
FILE_CREATE NTFileCreationDisposition = 0x02
|
||||||
|
FILE_OPEN_IF NTFileCreationDisposition = 0x03
|
||||||
|
FILE_OVERWRITE NTFileCreationDisposition = 0x04
|
||||||
|
FILE_OVERWRITE_IF NTFileCreationDisposition = 0x05
|
||||||
|
FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05
|
||||||
|
)
|
||||||
|
|
||||||
// CreateFile and co. take flags or attributes together as one parameter.
|
// CreateFile and co. take flags or attributes together as one parameter.
|
||||||
// Define alias until we can use generics to allow both
|
// Define alias until we can use generics to allow both
|
||||||
|
//
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||||
type FileFlagOrAttribute uint32
|
type FileFlagOrAttribute uint32
|
||||||
|
|
||||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
const ( // from winnt.h
|
const (
|
||||||
|
// from winnt.h
|
||||||
|
|
||||||
FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
|
FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
|
||||||
FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
|
FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
|
||||||
FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
|
FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
|
||||||
|
@ -145,17 +171,51 @@ const ( // from winnt.h
|
||||||
FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
|
FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NtCreate* functions take a dedicated CreateOptions parameter.
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file
|
||||||
|
type NTCreateOptions uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// From ntioapi.h
|
||||||
|
|
||||||
|
FILE_DIRECTORY_FILE NTCreateOptions = 0x0000_0001
|
||||||
|
FILE_WRITE_THROUGH NTCreateOptions = 0x0000_0002
|
||||||
|
FILE_SEQUENTIAL_ONLY NTCreateOptions = 0x0000_0004
|
||||||
|
FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008
|
||||||
|
|
||||||
|
FILE_SYNCHRONOUS_IO_ALERT NTCreateOptions = 0x0000_0010
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020
|
||||||
|
FILE_NON_DIRECTORY_FILE NTCreateOptions = 0x0000_0040
|
||||||
|
FILE_CREATE_TREE_CONNECTION NTCreateOptions = 0x0000_0080
|
||||||
|
|
||||||
|
FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100
|
||||||
|
FILE_NO_EA_KNOWLEDGE NTCreateOptions = 0x0000_0200
|
||||||
|
FILE_DISABLE_TUNNELING NTCreateOptions = 0x0000_0400
|
||||||
|
FILE_RANDOM_ACCESS NTCreateOptions = 0x0000_0800
|
||||||
|
|
||||||
|
FILE_DELETE_ON_CLOSE NTCreateOptions = 0x0000_1000
|
||||||
|
FILE_OPEN_BY_FILE_ID NTCreateOptions = 0x0000_2000
|
||||||
|
FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000
|
||||||
|
FILE_NO_COMPRESSION NTCreateOptions = 0x0000_8000
|
||||||
|
)
|
||||||
|
|
||||||
type FileSQSFlag = FileFlagOrAttribute
|
type FileSQSFlag = FileFlagOrAttribute
|
||||||
|
|
||||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
const ( // from winbase.h
|
const (
|
||||||
|
// from winbase.h
|
||||||
|
|
||||||
SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
|
SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
|
||||||
SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
|
SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
|
||||||
SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
|
SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
|
||||||
SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
|
SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
|
||||||
|
|
||||||
SECURITY_SQOS_PRESENT FileSQSFlag = 0x00100000
|
SECURITY_SQOS_PRESENT FileSQSFlag = 0x0010_0000
|
||||||
SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F0000
|
SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetFinalPathNameByHandle flags
|
// GetFinalPathNameByHandle flags
|
||||||
|
|
|
@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_IO_PENDING
|
return errERROR_IO_PENDING
|
||||||
}
|
}
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +42,7 @@ var (
|
||||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
func CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||||
var _p0 *uint16
|
var _p0 *uint16
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,8 +51,8 @@ func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.
|
||||||
return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile))
|
||||||
handle = windows.Handle(r0)
|
handle = windows.Handle(r0)
|
||||||
if handle == windows.InvalidHandle {
|
if handle == windows.InvalidHandle {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
|
|
|
@ -156,9 +156,7 @@ func connectEx(
|
||||||
bytesSent *uint32,
|
bytesSent *uint32,
|
||||||
overlapped *windows.Overlapped,
|
overlapped *windows.Overlapped,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
// todo: after upgrading to 1.18, switch from syscall.Syscall9 to syscall.SyscallN
|
r1, _, e1 := syscall.SyscallN(connectExFunc.addr,
|
||||||
r1, _, e1 := syscall.Syscall9(connectExFunc.addr,
|
|
||||||
7,
|
|
||||||
uintptr(s),
|
uintptr(s),
|
||||||
uintptr(name),
|
uintptr(name),
|
||||||
uintptr(namelen),
|
uintptr(namelen),
|
||||||
|
@ -166,8 +164,8 @@ func connectEx(
|
||||||
uintptr(sendDataLen),
|
uintptr(sendDataLen),
|
||||||
uintptr(unsafe.Pointer(bytesSent)),
|
uintptr(unsafe.Pointer(bytesSent)),
|
||||||
uintptr(unsafe.Pointer(overlapped)),
|
uintptr(unsafe.Pointer(overlapped)),
|
||||||
0,
|
)
|
||||||
0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = error(e1)
|
err = error(e1)
|
||||||
|
|
|
@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_IO_PENDING
|
return errERROR_IO_PENDING
|
||||||
}
|
}
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +45,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +53,7 @@ func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -64,7 +61,7 @@ func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (b *WString) Free() {
|
||||||
// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
|
// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
|
||||||
// previous buffer back into pool.
|
// previous buffer back into pool.
|
||||||
func (b *WString) ResizeTo(c uint32) uint32 {
|
func (b *WString) ResizeTo(c uint32) uint32 {
|
||||||
// allready sufficient (or n is 0)
|
// already sufficient (or n is 0)
|
||||||
if c <= b.Cap() {
|
if c <= b.Cap() {
|
||||||
return b.Cap()
|
return b.Cap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
@ -20,20 +19,44 @@ import (
|
||||||
"github.com/Microsoft/go-winio/internal/fs"
|
"github.com/Microsoft/go-winio/internal/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
//sys connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) = ConnectNamedPipe
|
||||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateNamedPipeW
|
||||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
//sys disconnectNamedPipe(pipe windows.Handle) (err error) = DisconnectNamedPipe
|
||||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
//sys getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||||
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
//sys getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
|
//sys ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
|
||||||
//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||||
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
|
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
|
||||||
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
|
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
|
||||||
|
|
||||||
|
type PipeConn interface {
|
||||||
|
net.Conn
|
||||||
|
Disconnect() error
|
||||||
|
Flush() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// type aliases for mkwinsyscall code
|
||||||
|
type (
|
||||||
|
ntAccessMask = fs.AccessMask
|
||||||
|
ntFileShareMode = fs.FileShareMode
|
||||||
|
ntFileCreationDisposition = fs.NTFileCreationDisposition
|
||||||
|
ntFileOptions = fs.NTCreateOptions
|
||||||
|
)
|
||||||
|
|
||||||
type ioStatusBlock struct {
|
type ioStatusBlock struct {
|
||||||
Status, Information uintptr
|
Status, Information uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typedef struct _OBJECT_ATTRIBUTES {
|
||||||
|
// ULONG Length;
|
||||||
|
// HANDLE RootDirectory;
|
||||||
|
// PUNICODE_STRING ObjectName;
|
||||||
|
// ULONG Attributes;
|
||||||
|
// PVOID SecurityDescriptor;
|
||||||
|
// PVOID SecurityQualityOfService;
|
||||||
|
// } OBJECT_ATTRIBUTES;
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes
|
||||||
type objectAttributes struct {
|
type objectAttributes struct {
|
||||||
Length uintptr
|
Length uintptr
|
||||||
RootDirectory uintptr
|
RootDirectory uintptr
|
||||||
|
@ -49,6 +72,17 @@ type unicodeString struct {
|
||||||
Buffer uintptr
|
Buffer uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typedef struct _SECURITY_DESCRIPTOR {
|
||||||
|
// BYTE Revision;
|
||||||
|
// BYTE Sbz1;
|
||||||
|
// SECURITY_DESCRIPTOR_CONTROL Control;
|
||||||
|
// PSID Owner;
|
||||||
|
// PSID Group;
|
||||||
|
// PACL Sacl;
|
||||||
|
// PACL Dacl;
|
||||||
|
// } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor
|
||||||
type securityDescriptor struct {
|
type securityDescriptor struct {
|
||||||
Revision byte
|
Revision byte
|
||||||
Sbz1 byte
|
Sbz1 byte
|
||||||
|
@ -80,6 +114,8 @@ type win32Pipe struct {
|
||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ PipeConn = (*win32Pipe)(nil)
|
||||||
|
|
||||||
type win32MessageBytePipe struct {
|
type win32MessageBytePipe struct {
|
||||||
win32Pipe
|
win32Pipe
|
||||||
writeClosed bool
|
writeClosed bool
|
||||||
|
@ -103,6 +139,10 @@ func (f *win32Pipe) SetDeadline(t time.Time) error {
|
||||||
return f.SetWriteDeadline(t)
|
return f.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) Disconnect() error {
|
||||||
|
return disconnectNamedPipe(f.win32File.handle)
|
||||||
|
}
|
||||||
|
|
||||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||||
func (f *win32MessageBytePipe) CloseWrite() error {
|
func (f *win32MessageBytePipe) CloseWrite() error {
|
||||||
if f.writeClosed {
|
if f.writeClosed {
|
||||||
|
@ -146,7 +186,7 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
||||||
// zero-byte message, ensure that all future Read() calls
|
// zero-byte message, ensure that all future Read() calls
|
||||||
// also return EOF.
|
// also return EOF.
|
||||||
f.readEOF = true
|
f.readEOF = true
|
||||||
} else if err == syscall.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
|
} else if err == windows.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
|
||||||
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
||||||
// and the message still has more bytes. Treat this as a success, since
|
// and the message still has more bytes. Treat this as a success, since
|
||||||
// this package presents all named pipes as byte streams.
|
// this package presents all named pipes as byte streams.
|
||||||
|
@ -164,21 +204,20 @@ func (s pipeAddress) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
||||||
func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask) (syscall.Handle, error) {
|
func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask, impLevel PipeImpLevel) (windows.Handle, error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return syscall.Handle(0), ctx.Err()
|
return windows.Handle(0), ctx.Err()
|
||||||
default:
|
default:
|
||||||
wh, err := fs.CreateFile(*path,
|
h, err := fs.CreateFile(*path,
|
||||||
access,
|
access,
|
||||||
0, // mode
|
0, // mode
|
||||||
nil, // security attributes
|
nil, // security attributes
|
||||||
fs.OPEN_EXISTING,
|
fs.OPEN_EXISTING,
|
||||||
fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.SECURITY_ANONYMOUS,
|
fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.FileSQSFlag(impLevel),
|
||||||
0, // template file handle
|
0, // template file handle
|
||||||
)
|
)
|
||||||
h := syscall.Handle(wh)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
@ -214,15 +253,33 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
||||||
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
||||||
// cancellation or timeout.
|
// cancellation or timeout.
|
||||||
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
||||||
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
|
return DialPipeAccess(ctx, path, uint32(fs.GENERIC_READ|fs.GENERIC_WRITE))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PipeImpLevel is an enumeration of impersonation levels that may be set
|
||||||
|
// when calling DialPipeAccessImpersonation.
|
||||||
|
type PipeImpLevel uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PipeImpLevelAnonymous = PipeImpLevel(fs.SECURITY_ANONYMOUS)
|
||||||
|
PipeImpLevelIdentification = PipeImpLevel(fs.SECURITY_IDENTIFICATION)
|
||||||
|
PipeImpLevelImpersonation = PipeImpLevel(fs.SECURITY_IMPERSONATION)
|
||||||
|
PipeImpLevelDelegation = PipeImpLevel(fs.SECURITY_DELEGATION)
|
||||||
|
)
|
||||||
|
|
||||||
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
||||||
// cancellation or timeout.
|
// cancellation or timeout.
|
||||||
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
||||||
|
return DialPipeAccessImpLevel(ctx, path, access, PipeImpLevelAnonymous)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPipeAccessImpLevel attempts to connect to a named pipe by `path` with
|
||||||
|
// `access` at `impLevel` until `ctx` cancellation or timeout. The other
|
||||||
|
// DialPipe* implementations use PipeImpLevelAnonymous.
|
||||||
|
func DialPipeAccessImpLevel(ctx context.Context, path string, access uint32, impLevel PipeImpLevel) (net.Conn, error) {
|
||||||
var err error
|
var err error
|
||||||
var h syscall.Handle
|
var h windows.Handle
|
||||||
h, err = tryDialPipe(ctx, &path, fs.AccessMask(access))
|
h, err = tryDialPipe(ctx, &path, fs.AccessMask(access), impLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -235,7 +292,7 @@ func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn,
|
||||||
|
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(h)
|
windows.Close(h)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +312,7 @@ type acceptResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type win32PipeListener struct {
|
type win32PipeListener struct {
|
||||||
firstHandle syscall.Handle
|
firstHandle windows.Handle
|
||||||
path string
|
path string
|
||||||
config PipeConfig
|
config PipeConfig
|
||||||
acceptCh chan (chan acceptResponse)
|
acceptCh chan (chan acceptResponse)
|
||||||
|
@ -263,8 +320,8 @@ type win32PipeListener struct {
|
||||||
doneCh chan int
|
doneCh chan int
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (windows.Handle, error) {
|
||||||
path16, err := syscall.UTF16FromString(path)
|
path16, err := windows.UTF16FromString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
|
@ -280,16 +337,20 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
||||||
).Err(); err != nil {
|
).Err(); err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
defer localFree(ntPath.Buffer)
|
defer windows.LocalFree(windows.Handle(ntPath.Buffer)) //nolint:errcheck
|
||||||
oa.ObjectName = &ntPath
|
oa.ObjectName = &ntPath
|
||||||
oa.Attributes = windows.OBJ_CASE_INSENSITIVE
|
oa.Attributes = windows.OBJ_CASE_INSENSITIVE
|
||||||
|
|
||||||
// The security descriptor is only needed for the first pipe.
|
// The security descriptor is only needed for the first pipe.
|
||||||
if first {
|
if first {
|
||||||
if sd != nil {
|
if sd != nil {
|
||||||
|
//todo: does `sdb` need to be allocated on the heap, or can go allocate it?
|
||||||
l := uint32(len(sd))
|
l := uint32(len(sd))
|
||||||
sdb := localAlloc(0, l)
|
sdb, err := windows.LocalAlloc(0, l)
|
||||||
defer localFree(sdb)
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("LocalAlloc for security descriptor with of length %d: %w", l, err)
|
||||||
|
}
|
||||||
|
defer windows.LocalFree(windows.Handle(sdb)) //nolint:errcheck
|
||||||
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
||||||
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
||||||
} else {
|
} else {
|
||||||
|
@ -298,7 +359,7 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
||||||
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
||||||
return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
|
return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
|
||||||
}
|
}
|
||||||
defer localFree(dacl)
|
defer windows.LocalFree(windows.Handle(dacl)) //nolint:errcheck
|
||||||
|
|
||||||
sdb := &securityDescriptor{
|
sdb := &securityDescriptor{
|
||||||
Revision: 1,
|
Revision: 1,
|
||||||
|
@ -314,27 +375,27 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
||||||
typ |= windows.FILE_PIPE_MESSAGE_TYPE
|
typ |= windows.FILE_PIPE_MESSAGE_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
disposition := uint32(windows.FILE_OPEN)
|
disposition := fs.FILE_OPEN
|
||||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
|
access := fs.GENERIC_READ | fs.GENERIC_WRITE | fs.SYNCHRONIZE
|
||||||
if first {
|
if first {
|
||||||
disposition = windows.FILE_CREATE
|
disposition = fs.FILE_CREATE
|
||||||
// By not asking for read or write access, the named pipe file system
|
// By not asking for read or write access, the named pipe file system
|
||||||
// will put this pipe into an initially disconnected state, blocking
|
// will put this pipe into an initially disconnected state, blocking
|
||||||
// client connections until the next call with first == false.
|
// client connections until the next call with first == false.
|
||||||
access = syscall.SYNCHRONIZE
|
access = fs.SYNCHRONIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout := int64(-50 * 10000) // 50ms
|
timeout := int64(-50 * 10000) // 50ms
|
||||||
|
|
||||||
var (
|
var (
|
||||||
h syscall.Handle
|
h windows.Handle
|
||||||
iosb ioStatusBlock
|
iosb ioStatusBlock
|
||||||
)
|
)
|
||||||
err = ntCreateNamedPipeFile(&h,
|
err = ntCreateNamedPipeFile(&h,
|
||||||
access,
|
access,
|
||||||
&oa,
|
&oa,
|
||||||
&iosb,
|
&iosb,
|
||||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE,
|
fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE,
|
||||||
disposition,
|
disposition,
|
||||||
0,
|
0,
|
||||||
typ,
|
typ,
|
||||||
|
@ -359,7 +420,7 @@ func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
||||||
}
|
}
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(h)
|
windows.Close(h)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return f, nil
|
return f, nil
|
||||||
|
@ -418,7 +479,7 @@ func (l *win32PipeListener) listenerRoutine() {
|
||||||
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
|
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syscall.Close(l.firstHandle)
|
windows.Close(l.firstHandle)
|
||||||
l.firstHandle = 0
|
l.firstHandle = 0
|
||||||
// Notify Close() and Accept() callers that the handle has been closed.
|
// Notify Close() and Accept() callers that the handle has been closed.
|
||||||
close(l.doneCh)
|
close(l.doneCh)
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
|
@ -18,8 +17,8 @@ import (
|
||||||
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
//sys openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
//sys getCurrentThread() (h windows.Handle) = GetCurrentThread
|
||||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||||
|
@ -29,7 +28,7 @@ const (
|
||||||
SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
|
SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
|
||||||
|
|
||||||
//revive:disable-next-line:var-naming ALL_CAPS
|
//revive:disable-next-line:var-naming ALL_CAPS
|
||||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = windows.ERROR_NOT_ALL_ASSIGNED
|
ERROR_NOT_ALL_ASSIGNED windows.Errno = windows.ERROR_NOT_ALL_ASSIGNED
|
||||||
|
|
||||||
SeBackupPrivilege = "SeBackupPrivilege"
|
SeBackupPrivilege = "SeBackupPrivilege"
|
||||||
SeRestorePrivilege = "SeRestorePrivilege"
|
SeRestorePrivilege = "SeRestorePrivilege"
|
||||||
|
@ -177,7 +176,7 @@ func newThreadToken() (windows.Token, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var token windows.Token
|
var token windows.Token
|
||||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
err = openThreadToken(getCurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, false, &token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rerr := revertToSelf()
|
rerr := revertToSelf()
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
|
|
|
@ -5,7 +5,7 @@ package winio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"syscall"
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
|
@ -15,10 +15,6 @@ import (
|
||||||
//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
|
//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
|
||||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
|
//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
|
||||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
|
||||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
|
||||||
//sys localFree(mem uintptr) = LocalFree
|
|
||||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
|
||||||
|
|
||||||
type AccountLookupError struct {
|
type AccountLookupError struct {
|
||||||
Name string
|
Name string
|
||||||
|
@ -64,7 +60,7 @@ func LookupSidByName(name string) (sid string, err error) {
|
||||||
|
|
||||||
var sidSize, sidNameUse, refDomainSize uint32
|
var sidSize, sidNameUse, refDomainSize uint32
|
||||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
|
if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
|
||||||
return "", &AccountLookupError{name, err}
|
return "", &AccountLookupError{name, err}
|
||||||
}
|
}
|
||||||
sidBuffer := make([]byte, sidSize)
|
sidBuffer := make([]byte, sidSize)
|
||||||
|
@ -78,8 +74,8 @@ func LookupSidByName(name string) (sid string, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", &AccountLookupError{name, err}
|
return "", &AccountLookupError{name, err}
|
||||||
}
|
}
|
||||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
sid = windows.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||||
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
_, _ = windows.LocalFree(windows.Handle(unsafe.Pointer(strBuffer)))
|
||||||
return sid, nil
|
return sid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +96,7 @@ func LookupNameBySid(sid string) (name string, err error) {
|
||||||
if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
|
if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
|
||||||
return "", &AccountLookupError{sid, err}
|
return "", &AccountLookupError{sid, err}
|
||||||
}
|
}
|
||||||
defer localFree(uintptr(unsafe.Pointer(sidPtr)))
|
defer windows.LocalFree(windows.Handle(unsafe.Pointer(sidPtr))) //nolint:errcheck
|
||||||
|
|
||||||
var nameSize, refDomainSize, sidNameUse uint32
|
var nameSize, refDomainSize, sidNameUse uint32
|
||||||
err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
|
err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
|
||||||
|
@ -120,25 +116,18 @@ func LookupNameBySid(sid string) (name string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||||
var sdBuffer uintptr
|
sd, err := windows.SecurityDescriptorFromString(sddl)
|
||||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &SddlConversionError{sddl, err}
|
return nil, &SddlConversionError{Sddl: sddl, Err: err}
|
||||||
}
|
}
|
||||||
defer localFree(sdBuffer)
|
b := unsafe.Slice((*byte)(unsafe.Pointer(sd)), sd.Length())
|
||||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
return b, nil
|
||||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
|
||||||
return sd, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||||
var sddl *uint16
|
if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
|
||||||
// The returned string length seems to include an arbitrary number of terminating NULs.
|
return "", fmt.Errorf("SecurityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
|
||||||
// Don't use it.
|
|
||||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
s := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&sd[0]))
|
||||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
return s.String(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
//go:build tools
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import _ "golang.org/x/tools/cmd/stringer"
|
|
|
@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_IO_PENDING
|
return errERROR_IO_PENDING
|
||||||
}
|
}
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,11 +43,8 @@ var (
|
||||||
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
||||||
|
|
||||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
|
||||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
|
||||||
procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
|
procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
|
||||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
|
||||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||||
procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
|
procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
|
||||||
|
@ -65,12 +59,11 @@ var (
|
||||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||||
|
procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
|
||||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
|
||||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
|
||||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
||||||
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
||||||
|
@ -84,7 +77,7 @@ func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, ou
|
||||||
if releaseAll {
|
if releaseAll {
|
||||||
_p0 = 1
|
_p0 = 1
|
||||||
}
|
}
|
||||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
r0, _, e1 := syscall.SyscallN(procAdjustTokenPrivileges.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||||
success = r0 != 0
|
success = r0 != 0
|
||||||
if true {
|
if true {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
|
@ -92,33 +85,8 @@ func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, ou
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
|
||||||
if r1 == 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
r1, _, e1 := syscall.SyscallN(procConvertSidToStringSidW.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)))
|
||||||
if r1 == 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
|
||||||
var _p0 *uint16
|
|
||||||
_p0, err = syscall.UTF16PtrFromString(str)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -126,21 +94,15 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertStringSidToSid(str *uint16, sid **byte) (err error) {
|
func convertStringSidToSid(str *uint16, sid **byte) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)), 0)
|
r1, _, e1 := syscall.SyscallN(procConvertStringSidToSidW.Addr(), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
|
||||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
|
||||||
len = uint32(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func impersonateSelf(level uint32) (err error) {
|
func impersonateSelf(level uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procImpersonateSelf.Addr(), uintptr(level))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -157,7 +119,7 @@ func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSiz
|
||||||
}
|
}
|
||||||
|
|
||||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procLookupAccountNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -165,7 +127,7 @@ func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidS
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procLookupAccountSidW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -182,7 +144,7 @@ func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16,
|
||||||
}
|
}
|
||||||
|
|
||||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
r1, _, e1 := syscall.SyscallN(procLookupPrivilegeDisplayNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -199,7 +161,7 @@ func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *
|
||||||
}
|
}
|
||||||
|
|
||||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procLookupPrivilegeNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -221,19 +183,19 @@ func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err err
|
||||||
}
|
}
|
||||||
|
|
||||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
r1, _, e1 := syscall.SyscallN(procLookupPrivilegeValueW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
func openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||||
var _p0 uint32
|
var _p0 uint32
|
||||||
if openAsSelf {
|
if openAsSelf {
|
||||||
_p0 = 1
|
_p0 = 1
|
||||||
}
|
}
|
||||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procOpenThreadToken.Addr(), uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
@ -241,14 +203,14 @@ func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
func revertToSelf() (err error) {
|
func revertToSelf() (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
r1, _, e1 := syscall.SyscallN(procRevertToSelf.Addr())
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
func backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
var _p0 *byte
|
var _p0 *byte
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
_p0 = &b[0]
|
_p0 = &b[0]
|
||||||
|
@ -261,14 +223,14 @@ func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, proce
|
||||||
if processSecurity {
|
if processSecurity {
|
||||||
_p2 = 1
|
_p2 = 1
|
||||||
}
|
}
|
||||||
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procBackupRead.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
func backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
var _p0 *byte
|
var _p0 *byte
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
_p0 = &b[0]
|
_p0 = &b[0]
|
||||||
|
@ -281,39 +243,39 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
|
||||||
if processSecurity {
|
if processSecurity {
|
||||||
_p2 = 1
|
_p2 = 1
|
||||||
}
|
}
|
||||||
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
r1, _, e1 := syscall.SyscallN(procBackupWrite.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
func cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
r1, _, e1 := syscall.SyscallN(procCancelIoEx.Addr(), uintptr(file), uintptr(unsafe.Pointer(o)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
func connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
r1, _, e1 := syscall.SyscallN(procConnectNamedPipe.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(o)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
func createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) {
|
||||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
r0, _, e1 := syscall.SyscallN(procCreateIoCompletionPort.Addr(), uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount))
|
||||||
newport = syscall.Handle(r0)
|
newport = windows.Handle(r0)
|
||||||
if newport == 0 {
|
if newport == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
|
||||||
var _p0 *uint16
|
var _p0 *uint16
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -322,96 +284,93 @@ func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances ui
|
||||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
r0, _, e1 := syscall.SyscallN(procCreateNamedPipeW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)))
|
||||||
handle = syscall.Handle(r0)
|
handle = windows.Handle(r0)
|
||||||
if handle == syscall.InvalidHandle {
|
if handle == windows.InvalidHandle {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentThread() (h syscall.Handle) {
|
func disconnectNamedPipe(pipe windows.Handle) (err error) {
|
||||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
r1, _, e1 := syscall.SyscallN(procDisconnectNamedPipe.Addr(), uintptr(pipe))
|
||||||
h = syscall.Handle(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
func getCurrentThread() (h windows.Handle) {
|
||||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
r0, _, _ := syscall.SyscallN(procGetCurrentThread.Addr())
|
||||||
|
h = windows.Handle(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.SyscallN(procGetNamedPipeHandleStateW.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
func getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
r1, _, e1 := syscall.SyscallN(procGetNamedPipeInfo.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
func getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
r1, _, e1 := syscall.SyscallN(procGetQueuedCompletionStatus.Addr(), uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout))
|
||||||
ptr = uintptr(r0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func localFree(mem uintptr) {
|
|
||||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
|
||||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) {
|
func setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) {
|
||||||
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
|
r1, _, e1 := syscall.SyscallN(procSetFileCompletionNotificationModes.Addr(), uintptr(h), uintptr(flags))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) {
|
||||||
|
r0, _, _ := syscall.SyscallN(procNtCreateNamedPipeFile.Addr(), uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)))
|
||||||
status = ntStatus(r0)
|
status = ntStatus(r0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) {
|
func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) {
|
||||||
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
|
r0, _, _ := syscall.SyscallN(procRtlDefaultNpAcl.Addr(), uintptr(unsafe.Pointer(dacl)))
|
||||||
status = ntStatus(r0)
|
status = ntStatus(r0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) {
|
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) {
|
||||||
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
|
r0, _, _ := syscall.SyscallN(procRtlDosPathNameToNtPathName_U.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved))
|
||||||
status = ntStatus(r0)
|
status = ntStatus(r0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func rtlNtStatusToDosError(status ntStatus) (winerr error) {
|
func rtlNtStatusToDosError(status ntStatus) (winerr error) {
|
||||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
|
r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(status))
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
winerr = syscall.Errno(r0)
|
winerr = syscall.Errno(r0)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
func wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
||||||
var _p0 uint32
|
var _p0 uint32
|
||||||
if wait {
|
if wait {
|
||||||
_p0 = 1
|
_p0 = 1
|
||||||
}
|
}
|
||||||
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
|
r1, _, e1 := syscall.SyscallN(procWSAGetOverlappedResult.Addr(), uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,22 +0,0 @@
|
||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
Google as part of the Go project.
|
|
||||||
|
|
||||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation of Go, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by Google and acquired in
|
|
||||||
the future, licensable by Google that are necessarily infringed by this
|
|
||||||
implementation of Go. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation of Go or any code incorporated within this
|
|
||||||
implementation of Go constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation of Go
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
|
@ -1,401 +0,0 @@
|
||||||
// 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 semver implements comparison of semantic version strings.
|
|
||||||
// In this package, semantic version strings must begin with a leading "v",
|
|
||||||
// as in "v1.0.0".
|
|
||||||
//
|
|
||||||
// The general form of a semantic version string accepted by this package is
|
|
||||||
//
|
|
||||||
// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
|
|
||||||
//
|
|
||||||
// where square brackets indicate optional parts of the syntax;
|
|
||||||
// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
|
|
||||||
// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
|
|
||||||
// using only alphanumeric characters and hyphens; and
|
|
||||||
// all-numeric PRERELEASE identifiers must not have leading zeros.
|
|
||||||
//
|
|
||||||
// This package follows Semantic Versioning 2.0.0 (see semver.org)
|
|
||||||
// with two exceptions. First, it requires the "v" prefix. Second, it recognizes
|
|
||||||
// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
|
|
||||||
// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
|
|
||||||
package semver
|
|
||||||
|
|
||||||
import "sort"
|
|
||||||
|
|
||||||
// parsed returns the parsed form of a semantic version string.
|
|
||||||
type parsed struct {
|
|
||||||
major string
|
|
||||||
minor string
|
|
||||||
patch string
|
|
||||||
short string
|
|
||||||
prerelease string
|
|
||||||
build string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid reports whether v is a valid semantic version string.
|
|
||||||
func IsValid(v string) bool {
|
|
||||||
_, ok := parse(v)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canonical returns the canonical formatting of the semantic version v.
|
|
||||||
// It fills in any missing .MINOR or .PATCH and discards build metadata.
|
|
||||||
// Two semantic versions compare equal only if their canonical formattings
|
|
||||||
// are identical strings.
|
|
||||||
// The canonical invalid semantic version is the empty string.
|
|
||||||
func Canonical(v string) string {
|
|
||||||
p, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if p.build != "" {
|
|
||||||
return v[:len(v)-len(p.build)]
|
|
||||||
}
|
|
||||||
if p.short != "" {
|
|
||||||
return v + p.short
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Major returns the major version prefix of the semantic version v.
|
|
||||||
// For example, Major("v2.1.0") == "v2".
|
|
||||||
// If v is an invalid semantic version string, Major returns the empty string.
|
|
||||||
func Major(v string) string {
|
|
||||||
pv, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return v[:1+len(pv.major)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MajorMinor returns the major.minor version prefix of the semantic version v.
|
|
||||||
// For example, MajorMinor("v2.1.0") == "v2.1".
|
|
||||||
// If v is an invalid semantic version string, MajorMinor returns the empty string.
|
|
||||||
func MajorMinor(v string) string {
|
|
||||||
pv, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
i := 1 + len(pv.major)
|
|
||||||
if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor {
|
|
||||||
return v[:j]
|
|
||||||
}
|
|
||||||
return v[:i] + "." + pv.minor
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prerelease returns the prerelease suffix of the semantic version v.
|
|
||||||
// For example, Prerelease("v2.1.0-pre+meta") == "-pre".
|
|
||||||
// If v is an invalid semantic version string, Prerelease returns the empty string.
|
|
||||||
func Prerelease(v string) string {
|
|
||||||
pv, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return pv.prerelease
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build returns the build suffix of the semantic version v.
|
|
||||||
// For example, Build("v2.1.0+meta") == "+meta".
|
|
||||||
// If v is an invalid semantic version string, Build returns the empty string.
|
|
||||||
func Build(v string) string {
|
|
||||||
pv, ok := parse(v)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return pv.build
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare returns an integer comparing two versions according to
|
|
||||||
// semantic version precedence.
|
|
||||||
// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
|
|
||||||
//
|
|
||||||
// An invalid semantic version string is considered less than a valid one.
|
|
||||||
// All invalid semantic version strings compare equal to each other.
|
|
||||||
func Compare(v, w string) int {
|
|
||||||
pv, ok1 := parse(v)
|
|
||||||
pw, ok2 := parse(w)
|
|
||||||
if !ok1 && !ok2 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if !ok1 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if !ok2 {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
if c := compareInt(pv.major, pw.major); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if c := compareInt(pv.minor, pw.minor); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if c := compareInt(pv.patch, pw.patch); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return comparePrerelease(pv.prerelease, pw.prerelease)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max canonicalizes its arguments and then returns the version string
|
|
||||||
// that compares greater.
|
|
||||||
//
|
|
||||||
// Deprecated: use [Compare] instead. In most cases, returning a canonicalized
|
|
||||||
// version is not expected or desired.
|
|
||||||
func Max(v, w string) string {
|
|
||||||
v = Canonical(v)
|
|
||||||
w = Canonical(w)
|
|
||||||
if Compare(v, w) > 0 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByVersion implements [sort.Interface] for sorting semantic version strings.
|
|
||||||
type ByVersion []string
|
|
||||||
|
|
||||||
func (vs ByVersion) Len() int { return len(vs) }
|
|
||||||
func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
|
|
||||||
func (vs ByVersion) Less(i, j int) bool {
|
|
||||||
cmp := Compare(vs[i], vs[j])
|
|
||||||
if cmp != 0 {
|
|
||||||
return cmp < 0
|
|
||||||
}
|
|
||||||
return vs[i] < vs[j]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort sorts a list of semantic version strings using [ByVersion].
|
|
||||||
func Sort(list []string) {
|
|
||||||
sort.Sort(ByVersion(list))
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(v string) (p parsed, ok bool) {
|
|
||||||
if v == "" || v[0] != 'v' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.major, v, ok = parseInt(v[1:])
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v == "" {
|
|
||||||
p.minor = "0"
|
|
||||||
p.patch = "0"
|
|
||||||
p.short = ".0.0"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[0] != '.' {
|
|
||||||
ok = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.minor, v, ok = parseInt(v[1:])
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v == "" {
|
|
||||||
p.patch = "0"
|
|
||||||
p.short = ".0"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[0] != '.' {
|
|
||||||
ok = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.patch, v, ok = parseInt(v[1:])
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(v) > 0 && v[0] == '-' {
|
|
||||||
p.prerelease, v, ok = parsePrerelease(v)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(v) > 0 && v[0] == '+' {
|
|
||||||
p.build, v, ok = parseBuild(v)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if v != "" {
|
|
||||||
ok = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ok = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseInt(v string) (t, rest string, ok bool) {
|
|
||||||
if v == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[0] < '0' || '9' < v[0] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if v[0] == '0' && i != 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return v[:i], v[i:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func parsePrerelease(v string) (t, rest string, ok bool) {
|
|
||||||
// "A pre-release version MAY be denoted by appending a hyphen and
|
|
||||||
// a series of dot separated identifiers immediately following the patch version.
|
|
||||||
// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
|
|
||||||
// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
|
|
||||||
if v == "" || v[0] != '-' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
start := 1
|
|
||||||
for i < len(v) && v[i] != '+' {
|
|
||||||
if !isIdentChar(v[i]) && v[i] != '.' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[i] == '.' {
|
|
||||||
if start == i || isBadNum(v[start:i]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
start = i + 1
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if start == i || isBadNum(v[start:i]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return v[:i], v[i:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBuild(v string) (t, rest string, ok bool) {
|
|
||||||
if v == "" || v[0] != '+' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
start := 1
|
|
||||||
for i < len(v) {
|
|
||||||
if !isIdentChar(v[i]) && v[i] != '.' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v[i] == '.' {
|
|
||||||
if start == i {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
start = i + 1
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if start == i {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return v[:i], v[i:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentChar(c byte) bool {
|
|
||||||
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBadNum(v string) bool {
|
|
||||||
i := 0
|
|
||||||
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i == len(v) && i > 1 && v[0] == '0'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNum(v string) bool {
|
|
||||||
i := 0
|
|
||||||
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i == len(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareInt(x, y string) int {
|
|
||||||
if x == y {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if len(x) < len(y) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if len(x) > len(y) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
if x < y {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func comparePrerelease(x, y string) int {
|
|
||||||
// "When major, minor, and patch are equal, a pre-release version has
|
|
||||||
// lower precedence than a normal version.
|
|
||||||
// Example: 1.0.0-alpha < 1.0.0.
|
|
||||||
// Precedence for two pre-release versions with the same major, minor,
|
|
||||||
// and patch version MUST be determined by comparing each dot separated
|
|
||||||
// identifier from left to right until a difference is found as follows:
|
|
||||||
// identifiers consisting of only digits are compared numerically and
|
|
||||||
// identifiers with letters or hyphens are compared lexically in ASCII
|
|
||||||
// sort order. Numeric identifiers always have lower precedence than
|
|
||||||
// non-numeric identifiers. A larger set of pre-release fields has a
|
|
||||||
// higher precedence than a smaller set, if all of the preceding
|
|
||||||
// identifiers are equal.
|
|
||||||
// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
|
|
||||||
// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
|
|
||||||
if x == y {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if x == "" {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
if y == "" {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
for x != "" && y != "" {
|
|
||||||
x = x[1:] // skip - or .
|
|
||||||
y = y[1:] // skip - or .
|
|
||||||
var dx, dy string
|
|
||||||
dx, x = nextIdent(x)
|
|
||||||
dy, y = nextIdent(y)
|
|
||||||
if dx != dy {
|
|
||||||
ix := isNum(dx)
|
|
||||||
iy := isNum(dy)
|
|
||||||
if ix != iy {
|
|
||||||
if ix {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ix {
|
|
||||||
if len(dx) < len(dy) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if len(dx) > len(dy) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dx < dy {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if x == "" {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextIdent(x string) (dx, rest string) {
|
|
||||||
i := 0
|
|
||||||
for i < len(x) && x[i] != '.' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return x[:i], x[i:]
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,22 +0,0 @@
|
||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
Google as part of the Go project.
|
|
||||||
|
|
||||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation of Go, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by Google and acquired in
|
|
||||||
the future, licensable by Google that are necessarily infringed by this
|
|
||||||
implementation of Go. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation of Go or any code incorporated within this
|
|
||||||
implementation of Go constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation of Go
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
|
@ -1,660 +0,0 @@
|
||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
|
|
||||||
// interface. Given the name of a (signed or unsigned) integer type T that has constants
|
|
||||||
// defined, stringer will create a new self-contained Go source file implementing
|
|
||||||
//
|
|
||||||
// func (t T) String() string
|
|
||||||
//
|
|
||||||
// The file is created in the same package and directory as the package that defines T.
|
|
||||||
// It has helpful defaults designed for use with go generate.
|
|
||||||
//
|
|
||||||
// Stringer works best with constants that are consecutive values such as created using iota,
|
|
||||||
// but creates good code regardless. In the future it might also provide custom support for
|
|
||||||
// constant sets that are bit patterns.
|
|
||||||
//
|
|
||||||
// For example, given this snippet,
|
|
||||||
//
|
|
||||||
// package painkiller
|
|
||||||
//
|
|
||||||
// type Pill int
|
|
||||||
//
|
|
||||||
// const (
|
|
||||||
// Placebo Pill = iota
|
|
||||||
// Aspirin
|
|
||||||
// Ibuprofen
|
|
||||||
// Paracetamol
|
|
||||||
// Acetaminophen = Paracetamol
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// running this command
|
|
||||||
//
|
|
||||||
// stringer -type=Pill
|
|
||||||
//
|
|
||||||
// in the same directory will create the file pill_string.go, in package painkiller,
|
|
||||||
// containing a definition of
|
|
||||||
//
|
|
||||||
// func (Pill) String() string
|
|
||||||
//
|
|
||||||
// That method will translate the value of a Pill constant to the string representation
|
|
||||||
// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
|
|
||||||
// print the string "Aspirin".
|
|
||||||
//
|
|
||||||
// Typically this process would be run using go generate, like this:
|
|
||||||
//
|
|
||||||
// //go:generate stringer -type=Pill
|
|
||||||
//
|
|
||||||
// If multiple constants have the same value, the lexically first matching name will
|
|
||||||
// be used (in the example, Acetaminophen will print as "Paracetamol").
|
|
||||||
//
|
|
||||||
// With no arguments, it processes the package in the current directory.
|
|
||||||
// Otherwise, the arguments must name a single directory holding a Go package
|
|
||||||
// or a set of Go source files that represent a single Go package.
|
|
||||||
//
|
|
||||||
// The -type flag accepts a comma-separated list of types so a single run can
|
|
||||||
// generate methods for multiple types. The default output file is t_string.go,
|
|
||||||
// where t is the lower-cased name of the first type listed. It can be overridden
|
|
||||||
// with the -output flag.
|
|
||||||
//
|
|
||||||
// The -linecomment flag tells stringer to generate the text of any line comment, trimmed
|
|
||||||
// of leading spaces, instead of the constant name. For instance, if the constants above had a
|
|
||||||
// Pill prefix, one could write
|
|
||||||
//
|
|
||||||
// PillAspirin // Aspirin
|
|
||||||
//
|
|
||||||
// to suppress it in the output.
|
|
||||||
package main // import "golang.org/x/tools/cmd/stringer"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"go/ast"
|
|
||||||
"go/constant"
|
|
||||||
"go/format"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/go/packages"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
|
|
||||||
output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
|
|
||||||
trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
|
|
||||||
linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
|
|
||||||
buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Usage is a replacement usage function for the flags package.
|
|
||||||
func Usage() {
|
|
||||||
fmt.Fprintf(os.Stderr, "Usage of stringer:\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "For more information, see:\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "\thttps://pkg.go.dev/golang.org/x/tools/cmd/stringer\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "Flags:\n")
|
|
||||||
flag.PrintDefaults()
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
log.SetPrefix("stringer: ")
|
|
||||||
flag.Usage = Usage
|
|
||||||
flag.Parse()
|
|
||||||
if len(*typeNames) == 0 {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
types := strings.Split(*typeNames, ",")
|
|
||||||
var tags []string
|
|
||||||
if len(*buildTags) > 0 {
|
|
||||||
tags = strings.Split(*buildTags, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We accept either one directory or a list of files. Which do we have?
|
|
||||||
args := flag.Args()
|
|
||||||
if len(args) == 0 {
|
|
||||||
// Default: process whole package in current directory.
|
|
||||||
args = []string{"."}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the package once.
|
|
||||||
var dir string
|
|
||||||
g := Generator{
|
|
||||||
trimPrefix: *trimprefix,
|
|
||||||
lineComment: *linecomment,
|
|
||||||
}
|
|
||||||
// TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc).
|
|
||||||
if len(args) == 1 && isDirectory(args[0]) {
|
|
||||||
dir = args[0]
|
|
||||||
} else {
|
|
||||||
if len(tags) != 0 {
|
|
||||||
log.Fatal("-tags option applies only to directories, not when files are specified")
|
|
||||||
}
|
|
||||||
dir = filepath.Dir(args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
g.parsePackage(args, tags)
|
|
||||||
|
|
||||||
// Print the header and package clause.
|
|
||||||
g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
|
|
||||||
g.Printf("\n")
|
|
||||||
g.Printf("package %s", g.pkg.name)
|
|
||||||
g.Printf("\n")
|
|
||||||
g.Printf("import \"strconv\"\n") // Used by all methods.
|
|
||||||
|
|
||||||
// Run generate for each type.
|
|
||||||
for _, typeName := range types {
|
|
||||||
g.generate(typeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the output.
|
|
||||||
src := g.format()
|
|
||||||
|
|
||||||
// Write to file.
|
|
||||||
outputName := *output
|
|
||||||
if outputName == "" {
|
|
||||||
baseName := fmt.Sprintf("%s_string.go", types[0])
|
|
||||||
outputName = filepath.Join(dir, strings.ToLower(baseName))
|
|
||||||
}
|
|
||||||
err := os.WriteFile(outputName, src, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("writing output: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isDirectory reports whether the named file is a directory.
|
|
||||||
func isDirectory(name string) bool {
|
|
||||||
info, err := os.Stat(name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return info.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generator holds the state of the analysis. Primarily used to buffer
|
|
||||||
// the output for format.Source.
|
|
||||||
type Generator struct {
|
|
||||||
buf bytes.Buffer // Accumulated output.
|
|
||||||
pkg *Package // Package we are scanning.
|
|
||||||
|
|
||||||
trimPrefix string
|
|
||||||
lineComment bool
|
|
||||||
|
|
||||||
logf func(format string, args ...interface{}) // test logging hook; nil when not testing
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Generator) Printf(format string, args ...interface{}) {
|
|
||||||
fmt.Fprintf(&g.buf, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// File holds a single parsed file and associated data.
|
|
||||||
type File struct {
|
|
||||||
pkg *Package // Package to which this file belongs.
|
|
||||||
file *ast.File // Parsed AST.
|
|
||||||
// These fields are reset for each type being generated.
|
|
||||||
typeName string // Name of the constant type.
|
|
||||||
values []Value // Accumulator for constant values of that type.
|
|
||||||
|
|
||||||
trimPrefix string
|
|
||||||
lineComment bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Package struct {
|
|
||||||
name string
|
|
||||||
defs map[*ast.Ident]types.Object
|
|
||||||
files []*File
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsePackage analyzes the single package constructed from the patterns and tags.
|
|
||||||
// parsePackage exits if there is an error.
|
|
||||||
func (g *Generator) parsePackage(patterns []string, tags []string) {
|
|
||||||
cfg := &packages.Config{
|
|
||||||
Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
|
|
||||||
// TODO: Need to think about constants in test files. Maybe write type_string_test.go
|
|
||||||
// in a separate pass? For later.
|
|
||||||
Tests: false,
|
|
||||||
BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
|
|
||||||
Logf: g.logf,
|
|
||||||
}
|
|
||||||
pkgs, err := packages.Load(cfg, patterns...)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(pkgs) != 1 {
|
|
||||||
log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " "))
|
|
||||||
}
|
|
||||||
g.addPackage(pkgs[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// addPackage adds a type checked Package and its syntax files to the generator.
|
|
||||||
func (g *Generator) addPackage(pkg *packages.Package) {
|
|
||||||
g.pkg = &Package{
|
|
||||||
name: pkg.Name,
|
|
||||||
defs: pkg.TypesInfo.Defs,
|
|
||||||
files: make([]*File, len(pkg.Syntax)),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, file := range pkg.Syntax {
|
|
||||||
g.pkg.files[i] = &File{
|
|
||||||
file: file,
|
|
||||||
pkg: g.pkg,
|
|
||||||
trimPrefix: g.trimPrefix,
|
|
||||||
lineComment: g.lineComment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate produces the String method for the named type.
|
|
||||||
func (g *Generator) generate(typeName string) {
|
|
||||||
values := make([]Value, 0, 100)
|
|
||||||
for _, file := range g.pkg.files {
|
|
||||||
// Set the state for this run of the walker.
|
|
||||||
file.typeName = typeName
|
|
||||||
file.values = nil
|
|
||||||
if file.file != nil {
|
|
||||||
ast.Inspect(file.file, file.genDecl)
|
|
||||||
values = append(values, file.values...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(values) == 0 {
|
|
||||||
log.Fatalf("no values defined for type %s", typeName)
|
|
||||||
}
|
|
||||||
// Generate code that will fail if the constants change value.
|
|
||||||
g.Printf("func _() {\n")
|
|
||||||
g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n")
|
|
||||||
g.Printf("\t// Re-run the stringer command to generate them again.\n")
|
|
||||||
g.Printf("\tvar x [1]struct{}\n")
|
|
||||||
for _, v := range values {
|
|
||||||
g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str)
|
|
||||||
}
|
|
||||||
g.Printf("}\n")
|
|
||||||
runs := splitIntoRuns(values)
|
|
||||||
// The decision of which pattern to use depends on the number of
|
|
||||||
// runs in the numbers. If there's only one, it's easy. For more than
|
|
||||||
// one, there's a tradeoff between complexity and size of the data
|
|
||||||
// and code vs. the simplicity of a map. A map takes more space,
|
|
||||||
// but so does the code. The decision here (crossover at 10) is
|
|
||||||
// arbitrary, but considers that for large numbers of runs the cost
|
|
||||||
// of the linear scan in the switch might become important, and
|
|
||||||
// rather than use yet another algorithm such as binary search,
|
|
||||||
// we punt and use a map. In any case, the likelihood of a map
|
|
||||||
// being necessary for any realistic example other than bitmasks
|
|
||||||
// is very low. And bitmasks probably deserve their own analysis,
|
|
||||||
// to be done some other day.
|
|
||||||
switch {
|
|
||||||
case len(runs) == 1:
|
|
||||||
g.buildOneRun(runs, typeName)
|
|
||||||
case len(runs) <= 10:
|
|
||||||
g.buildMultipleRuns(runs, typeName)
|
|
||||||
default:
|
|
||||||
g.buildMap(runs, typeName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// splitIntoRuns breaks the values into runs of contiguous sequences.
|
|
||||||
// For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}.
|
|
||||||
// The input slice is known to be non-empty.
|
|
||||||
func splitIntoRuns(values []Value) [][]Value {
|
|
||||||
// We use stable sort so the lexically first name is chosen for equal elements.
|
|
||||||
sort.Stable(byValue(values))
|
|
||||||
// Remove duplicates. Stable sort has put the one we want to print first,
|
|
||||||
// so use that one. The String method won't care about which named constant
|
|
||||||
// was the argument, so the first name for the given value is the only one to keep.
|
|
||||||
// We need to do this because identical values would cause the switch or map
|
|
||||||
// to fail to compile.
|
|
||||||
j := 1
|
|
||||||
for i := 1; i < len(values); i++ {
|
|
||||||
if values[i].value != values[i-1].value {
|
|
||||||
values[j] = values[i]
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
values = values[:j]
|
|
||||||
runs := make([][]Value, 0, 10)
|
|
||||||
for len(values) > 0 {
|
|
||||||
// One contiguous sequence per outer loop.
|
|
||||||
i := 1
|
|
||||||
for i < len(values) && values[i].value == values[i-1].value+1 {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
runs = append(runs, values[:i])
|
|
||||||
values = values[i:]
|
|
||||||
}
|
|
||||||
return runs
|
|
||||||
}
|
|
||||||
|
|
||||||
// format returns the gofmt-ed contents of the Generator's buffer.
|
|
||||||
func (g *Generator) format() []byte {
|
|
||||||
src, err := format.Source(g.buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
// Should never happen, but can arise when developing this code.
|
|
||||||
// The user can compile the output to see the error.
|
|
||||||
log.Printf("warning: internal error: invalid Go generated: %s", err)
|
|
||||||
log.Printf("warning: compile the package to analyze the error")
|
|
||||||
return g.buf.Bytes()
|
|
||||||
}
|
|
||||||
return src
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value represents a declared constant.
|
|
||||||
type Value struct {
|
|
||||||
originalName string // The name of the constant.
|
|
||||||
name string // The name with trimmed prefix.
|
|
||||||
// The value is stored as a bit pattern alone. The boolean tells us
|
|
||||||
// whether to interpret it as an int64 or a uint64; the only place
|
|
||||||
// this matters is when sorting.
|
|
||||||
// Much of the time the str field is all we need; it is printed
|
|
||||||
// by Value.String.
|
|
||||||
value uint64 // Will be converted to int64 when needed.
|
|
||||||
signed bool // Whether the constant is a signed type.
|
|
||||||
str string // The string representation given by the "go/constant" package.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Value) String() string {
|
|
||||||
return v.str
|
|
||||||
}
|
|
||||||
|
|
||||||
// byValue lets us sort the constants into increasing order.
|
|
||||||
// We take care in the Less method to sort in signed or unsigned order,
|
|
||||||
// as appropriate.
|
|
||||||
type byValue []Value
|
|
||||||
|
|
||||||
func (b byValue) Len() int { return len(b) }
|
|
||||||
func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
|
||||||
func (b byValue) Less(i, j int) bool {
|
|
||||||
if b[i].signed {
|
|
||||||
return int64(b[i].value) < int64(b[j].value)
|
|
||||||
}
|
|
||||||
return b[i].value < b[j].value
|
|
||||||
}
|
|
||||||
|
|
||||||
// genDecl processes one declaration clause.
|
|
||||||
func (f *File) genDecl(node ast.Node) bool {
|
|
||||||
decl, ok := node.(*ast.GenDecl)
|
|
||||||
if !ok || decl.Tok != token.CONST {
|
|
||||||
// We only care about const declarations.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// The name of the type of the constants we are declaring.
|
|
||||||
// Can change if this is a multi-element declaration.
|
|
||||||
typ := ""
|
|
||||||
// Loop over the elements of the declaration. Each element is a ValueSpec:
|
|
||||||
// a list of names possibly followed by a type, possibly followed by values.
|
|
||||||
// If the type and value are both missing, we carry down the type (and value,
|
|
||||||
// but the "go/types" package takes care of that).
|
|
||||||
for _, spec := range decl.Specs {
|
|
||||||
vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
|
|
||||||
if vspec.Type == nil && len(vspec.Values) > 0 {
|
|
||||||
// "X = 1". With no type but a value. If the constant is untyped,
|
|
||||||
// skip this vspec and reset the remembered type.
|
|
||||||
typ = ""
|
|
||||||
|
|
||||||
// If this is a simple type conversion, remember the type.
|
|
||||||
// We don't mind if this is actually a call; a qualified call won't
|
|
||||||
// be matched (that will be SelectorExpr, not Ident), and only unusual
|
|
||||||
// situations will result in a function call that appears to be
|
|
||||||
// a type conversion.
|
|
||||||
ce, ok := vspec.Values[0].(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
id, ok := ce.Fun.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
typ = id.Name
|
|
||||||
}
|
|
||||||
if vspec.Type != nil {
|
|
||||||
// "X T". We have a type. Remember it.
|
|
||||||
ident, ok := vspec.Type.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
typ = ident.Name
|
|
||||||
}
|
|
||||||
if typ != f.typeName {
|
|
||||||
// This is not the type we're looking for.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// We now have a list of names (from one line of source code) all being
|
|
||||||
// declared with the desired type.
|
|
||||||
// Grab their names and actual values and store them in f.values.
|
|
||||||
for _, name := range vspec.Names {
|
|
||||||
if name.Name == "_" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// This dance lets the type checker find the values for us. It's a
|
|
||||||
// bit tricky: look up the object declared by the name, find its
|
|
||||||
// types.Const, and extract its value.
|
|
||||||
obj, ok := f.pkg.defs[name]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("no value for constant %s", name)
|
|
||||||
}
|
|
||||||
info := obj.Type().Underlying().(*types.Basic).Info()
|
|
||||||
if info&types.IsInteger == 0 {
|
|
||||||
log.Fatalf("can't handle non-integer constant type %s", typ)
|
|
||||||
}
|
|
||||||
value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
|
|
||||||
if value.Kind() != constant.Int {
|
|
||||||
log.Fatalf("can't happen: constant is not an integer %s", name)
|
|
||||||
}
|
|
||||||
i64, isInt := constant.Int64Val(value)
|
|
||||||
u64, isUint := constant.Uint64Val(value)
|
|
||||||
if !isInt && !isUint {
|
|
||||||
log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String())
|
|
||||||
}
|
|
||||||
if !isInt {
|
|
||||||
u64 = uint64(i64)
|
|
||||||
}
|
|
||||||
v := Value{
|
|
||||||
originalName: name.Name,
|
|
||||||
value: u64,
|
|
||||||
signed: info&types.IsUnsigned == 0,
|
|
||||||
str: value.String(),
|
|
||||||
}
|
|
||||||
if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 {
|
|
||||||
v.name = strings.TrimSpace(c.Text())
|
|
||||||
} else {
|
|
||||||
v.name = strings.TrimPrefix(v.originalName, f.trimPrefix)
|
|
||||||
}
|
|
||||||
f.values = append(f.values, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
|
|
||||||
// usize returns the number of bits of the smallest unsigned integer
|
|
||||||
// type that will hold n. Used to create the smallest possible slice of
|
|
||||||
// integers to use as indexes into the concatenated strings.
|
|
||||||
func usize(n int) int {
|
|
||||||
switch {
|
|
||||||
case n < 1<<8:
|
|
||||||
return 8
|
|
||||||
case n < 1<<16:
|
|
||||||
return 16
|
|
||||||
default:
|
|
||||||
// 2^32 is enough constants for anyone.
|
|
||||||
return 32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// declareIndexAndNameVars declares the index slices and concatenated names
|
|
||||||
// strings representing the runs of values.
|
|
||||||
func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
|
|
||||||
var indexes, names []string
|
|
||||||
for i, run := range runs {
|
|
||||||
index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
|
|
||||||
if len(run) != 1 {
|
|
||||||
indexes = append(indexes, index)
|
|
||||||
}
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
g.Printf("const (\n")
|
|
||||||
for _, name := range names {
|
|
||||||
g.Printf("\t%s\n", name)
|
|
||||||
}
|
|
||||||
g.Printf(")\n\n")
|
|
||||||
|
|
||||||
if len(indexes) > 0 {
|
|
||||||
g.Printf("var (")
|
|
||||||
for _, index := range indexes {
|
|
||||||
g.Printf("\t%s\n", index)
|
|
||||||
}
|
|
||||||
g.Printf(")\n\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
|
|
||||||
func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) {
|
|
||||||
index, name := g.createIndexAndNameDecl(run, typeName, "")
|
|
||||||
g.Printf("const %s\n", name)
|
|
||||||
g.Printf("var %s\n", index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var".
|
|
||||||
func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) {
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
indexes := make([]int, len(run))
|
|
||||||
for i := range run {
|
|
||||||
b.WriteString(run[i].name)
|
|
||||||
indexes[i] = b.Len()
|
|
||||||
}
|
|
||||||
nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String())
|
|
||||||
nameLen := b.Len()
|
|
||||||
b.Reset()
|
|
||||||
fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen))
|
|
||||||
for i, v := range indexes {
|
|
||||||
if i > 0 {
|
|
||||||
fmt.Fprintf(b, ", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "%d", v)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "}")
|
|
||||||
return b.String(), nameConst
|
|
||||||
}
|
|
||||||
|
|
||||||
// declareNameVars declares the concatenated names string representing all the values in the runs.
|
|
||||||
func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) {
|
|
||||||
g.Printf("const _%s_name%s = \"", typeName, suffix)
|
|
||||||
for _, run := range runs {
|
|
||||||
for i := range run {
|
|
||||||
g.Printf("%s", run[i].name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.Printf("\"\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildOneRun generates the variables and String method for a single run of contiguous values.
|
|
||||||
func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
|
|
||||||
values := runs[0]
|
|
||||||
g.Printf("\n")
|
|
||||||
g.declareIndexAndNameVar(values, typeName)
|
|
||||||
// The generated code is simple enough to write as a Printf format.
|
|
||||||
lessThanZero := ""
|
|
||||||
if values[0].signed {
|
|
||||||
lessThanZero = "i < 0 || "
|
|
||||||
}
|
|
||||||
if values[0].value == 0 { // Signed or unsigned, 0 is still 0.
|
|
||||||
g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero)
|
|
||||||
} else {
|
|
||||||
g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arguments to format are:
|
|
||||||
//
|
|
||||||
// [1]: type name
|
|
||||||
// [2]: size of index element (8 for uint8 etc.)
|
|
||||||
// [3]: less than zero check (for signed types)
|
|
||||||
const stringOneRun = `func (i %[1]s) String() string {
|
|
||||||
if %[3]si >= %[1]s(len(_%[1]s_index)-1) {
|
|
||||||
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
// Arguments to format are:
|
|
||||||
// [1]: type name
|
|
||||||
// [2]: lowest defined value for type, as a string
|
|
||||||
// [3]: size of index element (8 for uint8 etc.)
|
|
||||||
// [4]: less than zero check (for signed types)
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
const stringOneRunWithOffset = `func (i %[1]s) String() string {
|
|
||||||
i -= %[2]s
|
|
||||||
if %[4]si >= %[1]s(len(_%[1]s_index)-1) {
|
|
||||||
return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")"
|
|
||||||
}
|
|
||||||
return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
|
|
||||||
// For this pattern, a single Printf format won't do.
|
|
||||||
func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
|
|
||||||
g.Printf("\n")
|
|
||||||
g.declareIndexAndNameVars(runs, typeName)
|
|
||||||
g.Printf("func (i %s) String() string {\n", typeName)
|
|
||||||
g.Printf("\tswitch {\n")
|
|
||||||
for i, values := range runs {
|
|
||||||
if len(values) == 1 {
|
|
||||||
g.Printf("\tcase i == %s:\n", &values[0])
|
|
||||||
g.Printf("\t\treturn _%s_name_%d\n", typeName, i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if values[0].value == 0 && !values[0].signed {
|
|
||||||
// For an unsigned lower bound of 0, "0 <= i" would be redundant.
|
|
||||||
g.Printf("\tcase i <= %s:\n", &values[len(values)-1])
|
|
||||||
} else {
|
|
||||||
g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1])
|
|
||||||
}
|
|
||||||
if values[0].value != 0 {
|
|
||||||
g.Printf("\t\ti -= %s\n", &values[0])
|
|
||||||
}
|
|
||||||
g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n",
|
|
||||||
typeName, i, typeName, i, typeName, i)
|
|
||||||
}
|
|
||||||
g.Printf("\tdefault:\n")
|
|
||||||
g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName)
|
|
||||||
g.Printf("\t}\n")
|
|
||||||
g.Printf("}\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildMap handles the case where the space is so sparse a map is a reasonable fallback.
|
|
||||||
// It's a rare situation but has simple code.
|
|
||||||
func (g *Generator) buildMap(runs [][]Value, typeName string) {
|
|
||||||
g.Printf("\n")
|
|
||||||
g.declareNameVars(runs, typeName, "")
|
|
||||||
g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName)
|
|
||||||
n := 0
|
|
||||||
for _, values := range runs {
|
|
||||||
for _, value := range values {
|
|
||||||
g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name))
|
|
||||||
n += len(value.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.Printf("}\n\n")
|
|
||||||
g.Printf(stringMap, typeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Argument to format is the type name.
|
|
||||||
const stringMap = `func (i %[1]s) String() string {
|
|
||||||
if str, ok := _%[1]s_map[i]; ok {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,186 +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.
|
|
||||||
|
|
||||||
// Package gcexportdata provides functions for locating, reading, and
|
|
||||||
// writing export data files containing type information produced by the
|
|
||||||
// gc compiler. This package supports go1.7 export data format and all
|
|
||||||
// later versions.
|
|
||||||
//
|
|
||||||
// Although it might seem convenient for this package to live alongside
|
|
||||||
// go/types in the standard library, this would cause version skew
|
|
||||||
// problems for developer tools that use it, since they must be able to
|
|
||||||
// consume the outputs of the gc compiler both before and after a Go
|
|
||||||
// update such as from Go 1.7 to Go 1.8. Because this package lives in
|
|
||||||
// golang.org/x/tools, sites can update their version of this repo some
|
|
||||||
// time before the Go 1.8 release and rebuild and redeploy their
|
|
||||||
// developer tools, which will then be able to consume both Go 1.7 and
|
|
||||||
// Go 1.8 export data files, so they will work before and after the
|
|
||||||
// Go update. (See discussion at https://golang.org/issue/15651.)
|
|
||||||
package gcexportdata // import "golang.org/x/tools/go/gcexportdata"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"io"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/gcimporter"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Find returns the name of an object (.o) or archive (.a) file
|
|
||||||
// containing type information for the specified import path,
|
|
||||||
// using the go command.
|
|
||||||
// If no file was found, an empty filename is returned.
|
|
||||||
//
|
|
||||||
// A relative srcDir is interpreted relative to the current working directory.
|
|
||||||
//
|
|
||||||
// Find also returns the package's resolved (canonical) import path,
|
|
||||||
// reflecting the effects of srcDir and vendoring on importPath.
|
|
||||||
//
|
|
||||||
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
|
|
||||||
// which is more efficient.
|
|
||||||
func Find(importPath, srcDir string) (filename, path string) {
|
|
||||||
cmd := exec.Command("go", "list", "-json", "-export", "--", importPath)
|
|
||||||
cmd.Dir = srcDir
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
var data struct {
|
|
||||||
ImportPath string
|
|
||||||
Export string
|
|
||||||
}
|
|
||||||
json.Unmarshal(out, &data)
|
|
||||||
return data.Export, data.ImportPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewReader returns a reader for the export data section of an object
|
|
||||||
// (.o) or archive (.a) file read from r. The new reader may provide
|
|
||||||
// additional trailing data beyond the end of the export data.
|
|
||||||
func NewReader(r io.Reader) (io.Reader, error) {
|
|
||||||
buf := bufio.NewReader(r)
|
|
||||||
_, size, err := gcimporter.FindExportData(buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if size >= 0 {
|
|
||||||
// We were given an archive and found the __.PKGDEF in it.
|
|
||||||
// This tells us the size of the export data, and we don't
|
|
||||||
// need to return the entire file.
|
|
||||||
return &io.LimitedReader{
|
|
||||||
R: buf,
|
|
||||||
N: size,
|
|
||||||
}, nil
|
|
||||||
} else {
|
|
||||||
// We were given an object file. As such, we don't know how large
|
|
||||||
// the export data is and must return the entire file.
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// readAll works the same way as io.ReadAll, but avoids allocations and copies
|
|
||||||
// by preallocating a byte slice of the necessary size if the size is known up
|
|
||||||
// front. This is always possible when the input is an archive. In that case,
|
|
||||||
// NewReader will return the known size using an io.LimitedReader.
|
|
||||||
func readAll(r io.Reader) ([]byte, error) {
|
|
||||||
if lr, ok := r.(*io.LimitedReader); ok {
|
|
||||||
data := make([]byte, lr.N)
|
|
||||||
_, err := io.ReadFull(lr, data)
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
return io.ReadAll(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads export data from in, decodes it, and returns type
|
|
||||||
// information for the package.
|
|
||||||
//
|
|
||||||
// The package path (effectively its linker symbol prefix) is
|
|
||||||
// specified by path, since unlike the package name, this information
|
|
||||||
// may not be recorded in the export data.
|
|
||||||
//
|
|
||||||
// File position information is added to fset.
|
|
||||||
//
|
|
||||||
// Read may inspect and add to the imports map to ensure that references
|
|
||||||
// within the export data to other packages are consistent. The caller
|
|
||||||
// must ensure that imports[path] does not exist, or exists but is
|
|
||||||
// incomplete (see types.Package.Complete), and Read inserts the
|
|
||||||
// resulting package into this map entry.
|
|
||||||
//
|
|
||||||
// On return, the state of the reader is undefined.
|
|
||||||
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
|
||||||
data, err := readAll(in)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytes.HasPrefix(data, []byte("!<arch>")) {
|
|
||||||
return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The indexed export format starts with an 'i'; the older
|
|
||||||
// binary export format starts with a 'c', 'd', or 'v'
|
|
||||||
// (from "version"). Select appropriate importer.
|
|
||||||
if len(data) > 0 {
|
|
||||||
switch data[0] {
|
|
||||||
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)
|
|
||||||
return pkg, err
|
|
||||||
|
|
||||||
case 'u': // unified, from go1.20
|
|
||||||
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
|
|
||||||
return pkg, err
|
|
||||||
|
|
||||||
default:
|
|
||||||
l := len(data)
|
|
||||||
if l > 10 {
|
|
||||||
l = 10
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("empty export data for %s", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes encoded type information for the specified package to out.
|
|
||||||
// The FileSet provides file position information for named objects.
|
|
||||||
func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
|
|
||||||
if _, err := io.WriteString(out, "i"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gcimporter.IExportData(out, fset, pkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadBundle reads an export bundle from in, decodes it, and returns type
|
|
||||||
// information for the packages.
|
|
||||||
// File position information is added to fset.
|
|
||||||
//
|
|
||||||
// ReadBundle may inspect and add to the imports map to ensure that references
|
|
||||||
// within the export bundle to other packages are consistent.
|
|
||||||
//
|
|
||||||
// On return, the state of the reader is undefined.
|
|
||||||
//
|
|
||||||
// Experimental: This API is experimental and may change in the future.
|
|
||||||
func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) {
|
|
||||||
data, err := readAll(in)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading export bundle: %v", err)
|
|
||||||
}
|
|
||||||
return gcimporter.IImportBundle(fset, imports, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteBundle writes encoded type information for the specified packages to out.
|
|
||||||
// The FileSet provides file position information for named objects.
|
|
||||||
//
|
|
||||||
// Experimental: This API is experimental and may change in the future.
|
|
||||||
func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
|
|
||||||
return gcimporter.IExportBundle(out, fset, pkgs)
|
|
||||||
}
|
|
|
@ -1,75 +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.
|
|
||||||
|
|
||||||
package gcexportdata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewImporter returns a new instance of the types.Importer interface
|
|
||||||
// that reads type information from export data files written by gc.
|
|
||||||
// The Importer also satisfies types.ImporterFrom.
|
|
||||||
//
|
|
||||||
// Export data files are located using "go build" workspace conventions
|
|
||||||
// and the build.Default context.
|
|
||||||
//
|
|
||||||
// Use this importer instead of go/importer.For("gc", ...) to avoid the
|
|
||||||
// version-skew problems described in the documentation of this package,
|
|
||||||
// or to control the FileSet or access the imports map populated during
|
|
||||||
// package loading.
|
|
||||||
//
|
|
||||||
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
|
|
||||||
// which is more efficient.
|
|
||||||
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
|
|
||||||
return importer{fset, imports}
|
|
||||||
}
|
|
||||||
|
|
||||||
type importer struct {
|
|
||||||
fset *token.FileSet
|
|
||||||
imports map[string]*types.Package
|
|
||||||
}
|
|
||||||
|
|
||||||
func (imp importer) Import(importPath string) (*types.Package, error) {
|
|
||||||
return imp.ImportFrom(importPath, "", 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
|
|
||||||
filename, path := Find(importPath, srcDir)
|
|
||||||
if filename == "" {
|
|
||||||
if importPath == "unsafe" {
|
|
||||||
// Even for unsafe, call Find first in case
|
|
||||||
// the package was vendored.
|
|
||||||
return types.Unsafe, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("can't find import: %s", importPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
|
|
||||||
return pkg, nil // cache hit
|
|
||||||
}
|
|
||||||
|
|
||||||
// open file
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
f.Close()
|
|
||||||
if err != nil {
|
|
||||||
// add file name to error
|
|
||||||
err = fmt.Errorf("reading export data: %s: %v", filename, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
r, err := NewReader(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Read(r, imp.fset, imp.imports, path)
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
// 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 packagesdriver fetches type sizes for go/packages and go/analysis.
|
|
||||||
package packagesdriver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/gocommand"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) {
|
|
||||||
inv.Verb = "list"
|
|
||||||
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}
|
|
||||||
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
|
|
||||||
var goarch, compiler string
|
|
||||||
if rawErr != nil {
|
|
||||||
rawErrMsg := rawErr.Error()
|
|
||||||
if strings.Contains(rawErrMsg, "cannot find main module") ||
|
|
||||||
strings.Contains(rawErrMsg, "go.mod file not found") {
|
|
||||||
// User's running outside of a module.
|
|
||||||
// All bets are off. Get GOARCH and guess compiler is gc.
|
|
||||||
// TODO(matloob): Is this a problem in practice?
|
|
||||||
inv.Verb = "env"
|
|
||||||
inv.Args = []string{"GOARCH"}
|
|
||||||
envout, enverr := gocmdRunner.Run(ctx, inv)
|
|
||||||
if enverr != nil {
|
|
||||||
return "", "", enverr
|
|
||||||
}
|
|
||||||
goarch = strings.TrimSpace(envout.String())
|
|
||||||
compiler = "gc"
|
|
||||||
} else if friendlyErr != nil {
|
|
||||||
return "", "", friendlyErr
|
|
||||||
} else {
|
|
||||||
// This should be unreachable, but be defensive
|
|
||||||
// in case RunRaw's error results are inconsistent.
|
|
||||||
return "", "", rawErr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fields := strings.Fields(stdout.String())
|
|
||||||
if len(fields) < 2 {
|
|
||||||
return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
|
|
||||||
stdout.String(), stderr.String())
|
|
||||||
}
|
|
||||||
goarch = fields[0]
|
|
||||||
compiler = fields[1]
|
|
||||||
}
|
|
||||||
return compiler, goarch, nil
|
|
||||||
}
|
|
|
@ -1,220 +0,0 @@
|
||||||
// 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 packages loads Go packages for inspection and analysis.
|
|
||||||
|
|
||||||
The Load function takes as input a list of patterns and return a list of Package
|
|
||||||
structs describing individual packages matched by those patterns.
|
|
||||||
The LoadMode controls the amount of detail in the loaded packages.
|
|
||||||
|
|
||||||
Load passes most patterns directly to the underlying build tool,
|
|
||||||
but all patterns with the prefix "query=", where query is a
|
|
||||||
non-empty string of letters from [a-z], are reserved and may be
|
|
||||||
interpreted as query operators.
|
|
||||||
|
|
||||||
Two query operators are currently supported: "file" and "pattern".
|
|
||||||
|
|
||||||
The query "file=path/to/file.go" matches the package or packages enclosing
|
|
||||||
the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go"
|
|
||||||
might return the packages "fmt" and "fmt [fmt.test]".
|
|
||||||
|
|
||||||
The query "pattern=string" causes "string" to be passed directly to
|
|
||||||
the underlying build tool. In most cases this is unnecessary,
|
|
||||||
but an application can use Load("pattern=" + x) as an escaping mechanism
|
|
||||||
to ensure that x is not interpreted as a query operator if it contains '='.
|
|
||||||
|
|
||||||
All other query operators are reserved for future use and currently
|
|
||||||
cause Load to report an error.
|
|
||||||
|
|
||||||
The Package struct provides basic information about the package, including
|
|
||||||
|
|
||||||
- ID, a unique identifier for the package in the returned set;
|
|
||||||
- GoFiles, the names of the package's Go source files;
|
|
||||||
- Imports, a map from source import strings to the Packages they name;
|
|
||||||
- Types, the type information for the package's exported symbols;
|
|
||||||
- Syntax, the parsed syntax trees for the package's source code; and
|
|
||||||
- TypesInfo, the result of a complete type-check of the package syntax trees.
|
|
||||||
|
|
||||||
(See the documentation for type Package for the complete list of fields
|
|
||||||
and more detailed descriptions.)
|
|
||||||
|
|
||||||
For example,
|
|
||||||
|
|
||||||
Load(nil, "bytes", "unicode...")
|
|
||||||
|
|
||||||
returns four Package structs describing the standard library packages
|
|
||||||
bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern
|
|
||||||
can match multiple packages and that a package might be matched by
|
|
||||||
multiple patterns: in general it is not possible to determine which
|
|
||||||
packages correspond to which patterns.
|
|
||||||
|
|
||||||
Note that the list returned by Load contains only the packages matched
|
|
||||||
by the patterns. Their dependencies can be found by walking the import
|
|
||||||
graph using the Imports fields.
|
|
||||||
|
|
||||||
The Load function can be configured by passing a pointer to a Config as
|
|
||||||
the first argument. A nil Config is equivalent to the zero Config, which
|
|
||||||
causes Load to run in LoadFiles mode, collecting minimal information.
|
|
||||||
See the documentation for type Config for details.
|
|
||||||
|
|
||||||
As noted earlier, the Config.Mode controls the amount of detail
|
|
||||||
reported about the loaded packages. See the documentation for type LoadMode
|
|
||||||
for details.
|
|
||||||
|
|
||||||
Most tools should pass their command-line arguments (after any flags)
|
|
||||||
uninterpreted to the loader, so that the loader can interpret them
|
|
||||||
according to the conventions of the underlying build system.
|
|
||||||
See the Example function for typical usage.
|
|
||||||
*/
|
|
||||||
package packages // import "golang.org/x/tools/go/packages"
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Motivation and design considerations
|
|
||||||
|
|
||||||
The new package's design solves problems addressed by two existing
|
|
||||||
packages: go/build, which locates and describes packages, and
|
|
||||||
golang.org/x/tools/go/loader, which loads, parses and type-checks them.
|
|
||||||
The go/build.Package structure encodes too much of the 'go build' way
|
|
||||||
of organizing projects, leaving us in need of a data type that describes a
|
|
||||||
package of Go source code independent of the underlying build system.
|
|
||||||
We wanted something that works equally well with go build and vgo, and
|
|
||||||
also other build systems such as Bazel and Blaze, making it possible to
|
|
||||||
construct analysis tools that work in all these environments.
|
|
||||||
Tools such as errcheck and staticcheck were essentially unavailable to
|
|
||||||
the Go community at Google, and some of Google's internal tools for Go
|
|
||||||
are unavailable externally.
|
|
||||||
This new package provides a uniform way to obtain package metadata by
|
|
||||||
querying each of these build systems, optionally supporting their
|
|
||||||
preferred command-line notations for packages, so that tools integrate
|
|
||||||
neatly with users' build environments. The Metadata query function
|
|
||||||
executes an external query tool appropriate to the current workspace.
|
|
||||||
|
|
||||||
Loading packages always returns the complete import graph "all the way down",
|
|
||||||
even if all you want is information about a single package, because the query
|
|
||||||
mechanisms of all the build systems we currently support ({go,vgo} list, and
|
|
||||||
blaze/bazel aspect-based query) cannot provide detailed information
|
|
||||||
about one package without visiting all its dependencies too, so there is
|
|
||||||
no additional asymptotic cost to providing transitive information.
|
|
||||||
(This property might not be true of a hypothetical 5th build system.)
|
|
||||||
|
|
||||||
In calls to TypeCheck, all initial packages, and any package that
|
|
||||||
transitively depends on one of them, must be loaded from source.
|
|
||||||
Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from
|
|
||||||
source; D may be loaded from export data, and E may not be loaded at all
|
|
||||||
(though it's possible that D's export data mentions it, so a
|
|
||||||
types.Package may be created for it and exposed.)
|
|
||||||
|
|
||||||
The old loader had a feature to suppress type-checking of function
|
|
||||||
bodies on a per-package basis, primarily intended to reduce the work of
|
|
||||||
obtaining type information for imported packages. Now that imports are
|
|
||||||
satisfied by export data, the optimization no longer seems necessary.
|
|
||||||
|
|
||||||
Despite some early attempts, the old loader did not exploit export data,
|
|
||||||
instead always using the equivalent of WholeProgram mode. This was due
|
|
||||||
to the complexity of mixing source and export data packages (now
|
|
||||||
resolved by the upward traversal mentioned above), and because export data
|
|
||||||
files were nearly always missing or stale. Now that 'go build' supports
|
|
||||||
caching, all the underlying build systems can guarantee to produce
|
|
||||||
export data in a reasonable (amortized) time.
|
|
||||||
|
|
||||||
Test "main" packages synthesized by the build system are now reported as
|
|
||||||
first-class packages, avoiding the need for clients (such as go/ssa) to
|
|
||||||
reinvent this generation logic.
|
|
||||||
|
|
||||||
One way in which go/packages is simpler than the old loader is in its
|
|
||||||
treatment of in-package tests. In-package tests are packages that
|
|
||||||
consist of all the files of the library under test, plus the test files.
|
|
||||||
The old loader constructed in-package tests by a two-phase process of
|
|
||||||
mutation called "augmentation": first it would construct and type check
|
|
||||||
all the ordinary library packages and type-check the packages that
|
|
||||||
depend on them; then it would add more (test) files to the package and
|
|
||||||
type-check again. This two-phase approach had four major problems:
|
|
||||||
1) in processing the tests, the loader modified the library package,
|
|
||||||
leaving no way for a client application to see both the test
|
|
||||||
package and the library package; one would mutate into the other.
|
|
||||||
2) because test files can declare additional methods on types defined in
|
|
||||||
the library portion of the package, the dispatch of method calls in
|
|
||||||
the library portion was affected by the presence of the test files.
|
|
||||||
This should have been a clue that the packages were logically
|
|
||||||
different.
|
|
||||||
3) this model of "augmentation" assumed at most one in-package test
|
|
||||||
per library package, which is true of projects using 'go build',
|
|
||||||
but not other build systems.
|
|
||||||
4) because of the two-phase nature of test processing, all packages that
|
|
||||||
import the library package had to be processed before augmentation,
|
|
||||||
forcing a "one-shot" API and preventing the client from calling Load
|
|
||||||
in several times in sequence as is now possible in WholeProgram mode.
|
|
||||||
(TypeCheck mode has a similar one-shot restriction for a different reason.)
|
|
||||||
|
|
||||||
Early drafts of this package supported "multi-shot" operation.
|
|
||||||
Although it allowed clients to make a sequence of calls (or concurrent
|
|
||||||
calls) to Load, building up the graph of Packages incrementally,
|
|
||||||
it was of marginal value: it complicated the API
|
|
||||||
(since it allowed some options to vary across calls but not others),
|
|
||||||
it complicated the implementation,
|
|
||||||
it cannot be made to work in Types mode, as explained above,
|
|
||||||
and it was less efficient than making one combined call (when this is possible).
|
|
||||||
Among the clients we have inspected, none made multiple calls to load
|
|
||||||
but could not be easily and satisfactorily modified to make only a single call.
|
|
||||||
However, applications changes may be required.
|
|
||||||
For example, the ssadump command loads the user-specified packages
|
|
||||||
and in addition the runtime package. It is tempting to simply append
|
|
||||||
"runtime" to the user-provided list, but that does not work if the user
|
|
||||||
specified an ad-hoc package such as [a.go b.go].
|
|
||||||
Instead, ssadump no longer requests the runtime package,
|
|
||||||
but seeks it among the dependencies of the user-specified packages,
|
|
||||||
and emits an error if it is not found.
|
|
||||||
|
|
||||||
Overlays: The Overlay field in the Config allows providing alternate contents
|
|
||||||
for Go source files, by providing a mapping from file path to contents.
|
|
||||||
go/packages will pull in new imports added in overlay files when go/packages
|
|
||||||
is run in LoadImports mode or greater.
|
|
||||||
Overlay support for the go list driver isn't complete yet: if the file doesn't
|
|
||||||
exist on disk, it will only be recognized in an overlay if it is a non-test file
|
|
||||||
and the package would be reported even without the overlay.
|
|
||||||
|
|
||||||
Questions & Tasks
|
|
||||||
|
|
||||||
- Add GOARCH/GOOS?
|
|
||||||
They are not portable concepts, but could be made portable.
|
|
||||||
Our goal has been to allow users to express themselves using the conventions
|
|
||||||
of the underlying build system: if the build system honors GOARCH
|
|
||||||
during a build and during a metadata query, then so should
|
|
||||||
applications built atop that query mechanism.
|
|
||||||
Conversely, if the target architecture of the build is determined by
|
|
||||||
command-line flags, the application can pass the relevant
|
|
||||||
flags through to the build system using a command such as:
|
|
||||||
myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin"
|
|
||||||
However, this approach is low-level, unwieldy, and non-portable.
|
|
||||||
GOOS and GOARCH seem important enough to warrant a dedicated option.
|
|
||||||
|
|
||||||
- How should we handle partial failures such as a mixture of good and
|
|
||||||
malformed patterns, existing and non-existent packages, successful and
|
|
||||||
failed builds, import failures, import cycles, and so on, in a call to
|
|
||||||
Load?
|
|
||||||
|
|
||||||
- Support bazel, blaze, and go1.10 list, not just go1.11 list.
|
|
||||||
|
|
||||||
- Handle (and test) various partial success cases, e.g.
|
|
||||||
a mixture of good packages and:
|
|
||||||
invalid patterns
|
|
||||||
nonexistent packages
|
|
||||||
empty packages
|
|
||||||
packages with malformed package or import declarations
|
|
||||||
unreadable files
|
|
||||||
import cycles
|
|
||||||
other parse errors
|
|
||||||
type errors
|
|
||||||
Make sure we record errors at the correct place in the graph.
|
|
||||||
|
|
||||||
- Missing packages among initial arguments are not reported.
|
|
||||||
Return bogus packages for them, like golist does.
|
|
||||||
|
|
||||||
- "undeclared name" errors (for example) are reported out of source file
|
|
||||||
order. I suspect this is due to the breadth-first resolution now used
|
|
||||||
by go/types. Is that a bug? Discuss with gri.
|
|
||||||
|
|
||||||
*/
|
|
|
@ -1,101 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// This file enables an external tool to intercept package requests.
|
|
||||||
// If the tool is present then its results are used in preference to
|
|
||||||
// the go list command.
|
|
||||||
|
|
||||||
package packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The Driver Protocol
|
|
||||||
//
|
|
||||||
// The driver, given the inputs to a call to Load, returns metadata about the packages specified.
|
|
||||||
// This allows for different build systems to support go/packages by telling go/packages how the
|
|
||||||
// packages' source is organized.
|
|
||||||
// The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in
|
|
||||||
// the path as gopackagesdriver. It's given the inputs to load in its argv. See the package
|
|
||||||
// documentation in doc.go for the full description of the patterns that need to be supported.
|
|
||||||
// A driver receives as a JSON-serialized driverRequest struct in standard input and will
|
|
||||||
// produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output.
|
|
||||||
|
|
||||||
// driverRequest is used to provide the portion of Load's Config that is needed by a driver.
|
|
||||||
type driverRequest struct {
|
|
||||||
Mode LoadMode `json:"mode"`
|
|
||||||
// Env specifies the environment the underlying build system should be run in.
|
|
||||||
Env []string `json:"env"`
|
|
||||||
// BuildFlags are flags that should be passed to the underlying build system.
|
|
||||||
BuildFlags []string `json:"build_flags"`
|
|
||||||
// Tests specifies whether the patterns should also return test packages.
|
|
||||||
Tests bool `json:"tests"`
|
|
||||||
// Overlay maps file paths (relative to the driver's working directory) to the byte contents
|
|
||||||
// of overlay files.
|
|
||||||
Overlay map[string][]byte `json:"overlay"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// findExternalDriver returns the file path of a tool that supplies
|
|
||||||
// the build system package structure, or "" if not found."
|
|
||||||
// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
|
|
||||||
// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
|
|
||||||
func findExternalDriver(cfg *Config) driver {
|
|
||||||
const toolPrefix = "GOPACKAGESDRIVER="
|
|
||||||
tool := ""
|
|
||||||
for _, env := range cfg.Env {
|
|
||||||
if val := strings.TrimPrefix(env, toolPrefix); val != env {
|
|
||||||
tool = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tool != "" && tool == "off" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if tool == "" {
|
|
||||||
var err error
|
|
||||||
tool, err = exec.LookPath("gopackagesdriver")
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return func(cfg *Config, words ...string) (*driverResponse, error) {
|
|
||||||
req, err := json.Marshal(driverRequest{
|
|
||||||
Mode: cfg.Mode,
|
|
||||||
Env: cfg.Env,
|
|
||||||
BuildFlags: cfg.BuildFlags,
|
|
||||||
Tests: cfg.Tests,
|
|
||||||
Overlay: cfg.Overlay,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
stderr := new(bytes.Buffer)
|
|
||||||
cmd := exec.CommandContext(cfg.Context, tool, words...)
|
|
||||||
cmd.Dir = cfg.Dir
|
|
||||||
cmd.Env = cfg.Env
|
|
||||||
cmd.Stdin = bytes.NewReader(req)
|
|
||||||
cmd.Stdout = buf
|
|
||||||
cmd.Stderr = stderr
|
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
|
|
||||||
}
|
|
||||||
if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
var response driverResponse
|
|
||||||
if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &response, nil
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,83 +0,0 @@
|
||||||
// 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 packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/gocommand"
|
|
||||||
)
|
|
||||||
|
|
||||||
// determineRootDirs returns a mapping from absolute directories that could
|
|
||||||
// contain code to their corresponding import path prefixes.
|
|
||||||
func (state *golistState) determineRootDirs() (map[string]string, error) {
|
|
||||||
env, err := state.getEnv()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if env["GOMOD"] != "" {
|
|
||||||
state.rootsOnce.Do(func() {
|
|
||||||
state.rootDirs, state.rootDirsError = state.determineRootDirsModules()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
state.rootsOnce.Do(func() {
|
|
||||||
state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return state.rootDirs, state.rootDirsError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *golistState) determineRootDirsModules() (map[string]string, error) {
|
|
||||||
// List all of the modules--the first will be the directory for the main
|
|
||||||
// module. Any replaced modules will also need to be treated as roots.
|
|
||||||
// Editing files in the module cache isn't a great idea, so we don't
|
|
||||||
// plan to ever support that.
|
|
||||||
out, err := state.invokeGo("list", "-m", "-json", "all")
|
|
||||||
if err != nil {
|
|
||||||
// 'go list all' will fail if we're outside of a module and
|
|
||||||
// GO111MODULE=on. Try falling back without 'all'.
|
|
||||||
var innerErr error
|
|
||||||
out, innerErr = state.invokeGo("list", "-m", "-json")
|
|
||||||
if innerErr != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roots := map[string]string{}
|
|
||||||
modules := map[string]string{}
|
|
||||||
var i int
|
|
||||||
for dec := json.NewDecoder(out); dec.More(); {
|
|
||||||
mod := new(gocommand.ModuleJSON)
|
|
||||||
if err := dec.Decode(mod); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if mod.Dir != "" && mod.Path != "" {
|
|
||||||
// This is a valid module; add it to the map.
|
|
||||||
absDir, err := filepath.Abs(mod.Dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
modules[absDir] = mod.Path
|
|
||||||
// The first result is the main module.
|
|
||||||
if i == 0 || mod.Replace != nil && mod.Replace.Path != "" {
|
|
||||||
roots[absDir] = mod.Path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return roots, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
|
|
||||||
m := map[string]string{}
|
|
||||||
for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) {
|
|
||||||
absDir, err := filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m[filepath.Join(absDir, "src")] = ""
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
// 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 packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var allModes = []LoadMode{
|
|
||||||
NeedName,
|
|
||||||
NeedFiles,
|
|
||||||
NeedCompiledGoFiles,
|
|
||||||
NeedImports,
|
|
||||||
NeedDeps,
|
|
||||||
NeedExportFile,
|
|
||||||
NeedTypes,
|
|
||||||
NeedSyntax,
|
|
||||||
NeedTypesInfo,
|
|
||||||
NeedTypesSizes,
|
|
||||||
}
|
|
||||||
|
|
||||||
var modeStrings = []string{
|
|
||||||
"NeedName",
|
|
||||||
"NeedFiles",
|
|
||||||
"NeedCompiledGoFiles",
|
|
||||||
"NeedImports",
|
|
||||||
"NeedDeps",
|
|
||||||
"NeedExportFile",
|
|
||||||
"NeedTypes",
|
|
||||||
"NeedSyntax",
|
|
||||||
"NeedTypesInfo",
|
|
||||||
"NeedTypesSizes",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mod LoadMode) String() string {
|
|
||||||
m := mod
|
|
||||||
if m == 0 {
|
|
||||||
return "LoadMode(0)"
|
|
||||||
}
|
|
||||||
var out []string
|
|
||||||
for i, x := range allModes {
|
|
||||||
if x > m {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (m & x) != 0 {
|
|
||||||
out = append(out, modeStrings[i])
|
|
||||||
m = m ^ x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if m != 0 {
|
|
||||||
out = append(out, "Unknown")
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("LoadMode(%s)", strings.Join(out, "|"))
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,59 +0,0 @@
|
||||||
// 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 packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Visit visits all the packages in the import graph whose roots are
|
|
||||||
// pkgs, calling the optional pre function the first time each package
|
|
||||||
// is encountered (preorder), and the optional post function after a
|
|
||||||
// package's dependencies have been visited (postorder).
|
|
||||||
// The boolean result of pre(pkg) determines whether
|
|
||||||
// the imports of package pkg are visited.
|
|
||||||
func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
|
|
||||||
seen := make(map[*Package]bool)
|
|
||||||
var visit func(*Package)
|
|
||||||
visit = func(pkg *Package) {
|
|
||||||
if !seen[pkg] {
|
|
||||||
seen[pkg] = true
|
|
||||||
|
|
||||||
if pre == nil || pre(pkg) {
|
|
||||||
paths := make([]string, 0, len(pkg.Imports))
|
|
||||||
for path := range pkg.Imports {
|
|
||||||
paths = append(paths, path)
|
|
||||||
}
|
|
||||||
sort.Strings(paths) // Imports is a map, this makes visit stable
|
|
||||||
for _, path := range paths {
|
|
||||||
visit(pkg.Imports[path])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if post != nil {
|
|
||||||
post(pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
visit(pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintErrors prints to os.Stderr the accumulated errors of all
|
|
||||||
// packages in the import graph rooted at pkgs, dependencies first.
|
|
||||||
// PrintErrors returns the number of errors printed.
|
|
||||||
func PrintErrors(pkgs []*Package) int {
|
|
||||||
var n int
|
|
||||||
Visit(pkgs, nil, func(pkg *Package) {
|
|
||||||
for _, err := range pkg.Errors {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return n
|
|
||||||
}
|
|
|
@ -1,752 +0,0 @@
|
||||||
// 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"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/typeparams"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
// The method index here is always with respect
|
|
||||||
// to the underlying go/types data structures,
|
|
||||||
// which ultimately derives from source order
|
|
||||||
// and must be preserved by export data.
|
|
||||||
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)
|
|
||||||
|
|
||||||
// Method indices are w.r.t. the go/types data structures,
|
|
||||||
// ultimately deriving from source order,
|
|
||||||
// which is preserved by export data.
|
|
||||||
for i := 0; i < named.NumMethods(); i++ {
|
|
||||||
if named.Method(i) == 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) {
|
|
||||||
pathstr := string(p)
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
obj = t.Method(index)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
// 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 core provides support for event based telemetry.
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Event holds the information about an event of note that occurred.
|
|
||||||
type Event struct {
|
|
||||||
at time.Time
|
|
||||||
|
|
||||||
// As events are often on the stack, storing the first few labels directly
|
|
||||||
// in the event can avoid an allocation at all for the very common cases of
|
|
||||||
// simple events.
|
|
||||||
// The length needs to be large enough to cope with the majority of events
|
|
||||||
// but no so large as to cause undue stack pressure.
|
|
||||||
// A log message with two values will use 3 labels (one for each value and
|
|
||||||
// one for the message itself).
|
|
||||||
|
|
||||||
static [3]label.Label // inline storage for the first few labels
|
|
||||||
dynamic []label.Label // dynamically sized storage for remaining labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// eventLabelMap implements label.Map for a the labels of an Event.
|
|
||||||
type eventLabelMap struct {
|
|
||||||
event Event
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ev Event) At() time.Time { return ev.at }
|
|
||||||
|
|
||||||
func (ev Event) Format(f fmt.State, r rune) {
|
|
||||||
if !ev.at.IsZero() {
|
|
||||||
fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 "))
|
|
||||||
}
|
|
||||||
for index := 0; ev.Valid(index); index++ {
|
|
||||||
if l := ev.Label(index); l.Valid() {
|
|
||||||
fmt.Fprintf(f, "\n\t%v", l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ev Event) Valid(index int) bool {
|
|
||||||
return index >= 0 && index < len(ev.static)+len(ev.dynamic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ev Event) Label(index int) label.Label {
|
|
||||||
if index < len(ev.static) {
|
|
||||||
return ev.static[index]
|
|
||||||
}
|
|
||||||
return ev.dynamic[index-len(ev.static)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ev Event) Find(key label.Key) label.Label {
|
|
||||||
for _, l := range ev.static {
|
|
||||||
if l.Key() == key {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, l := range ev.dynamic {
|
|
||||||
if l.Key() == key {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return label.Label{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeEvent(static [3]label.Label, labels []label.Label) Event {
|
|
||||||
return Event{
|
|
||||||
static: static,
|
|
||||||
dynamic: labels,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloneEvent event returns a copy of the event with the time adjusted to at.
|
|
||||||
func CloneEvent(ev Event, at time.Time) Event {
|
|
||||||
ev.at = at
|
|
||||||
return ev
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
// 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 core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Exporter is a function that handles events.
|
|
||||||
// It may return a modified context and event.
|
|
||||||
type Exporter func(context.Context, Event, label.Map) context.Context
|
|
||||||
|
|
||||||
var (
|
|
||||||
exporter unsafe.Pointer
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetExporter sets the global exporter function that handles all events.
|
|
||||||
// The exporter is called synchronously from the event call site, so it should
|
|
||||||
// return quickly so as not to hold up user code.
|
|
||||||
func SetExporter(e Exporter) {
|
|
||||||
p := unsafe.Pointer(&e)
|
|
||||||
if e == nil {
|
|
||||||
// &e is always valid, and so p is always valid, but for the early abort
|
|
||||||
// of ProcessEvent to be efficient it needs to make the nil check on the
|
|
||||||
// pointer without having to dereference it, so we make the nil function
|
|
||||||
// also a nil pointer
|
|
||||||
p = nil
|
|
||||||
}
|
|
||||||
atomic.StorePointer(&exporter, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// deliver is called to deliver an event to the supplied exporter.
|
|
||||||
// it will fill in the time.
|
|
||||||
func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context {
|
|
||||||
// add the current time to the event
|
|
||||||
ev.at = time.Now()
|
|
||||||
// hand the event off to the current exporter
|
|
||||||
return exporter(ctx, ev, ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export is called to deliver an event to the global exporter if set.
|
|
||||||
func Export(ctx context.Context, ev Event) context.Context {
|
|
||||||
// get the global exporter and abort early if there is not one
|
|
||||||
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
|
|
||||||
if exporterPtr == nil {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
return deliver(ctx, *exporterPtr, ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportPair is called to deliver a start event to the supplied exporter.
|
|
||||||
// It also returns a function that will deliver the end event to the same
|
|
||||||
// exporter.
|
|
||||||
// It will fill in the time.
|
|
||||||
func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) {
|
|
||||||
// get the global exporter and abort early if there is not one
|
|
||||||
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
|
|
||||||
if exporterPtr == nil {
|
|
||||||
return ctx, func() {}
|
|
||||||
}
|
|
||||||
ctx = deliver(ctx, *exporterPtr, begin)
|
|
||||||
return ctx, func() { deliver(ctx, *exporterPtr, end) }
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
// 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 core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/keys"
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Log1 takes a message and one label delivers a log event to the exporter.
|
|
||||||
// It is a customized version of Print that is faster and does no allocation.
|
|
||||||
func Log1(ctx context.Context, message string, t1 label.Label) {
|
|
||||||
Export(ctx, MakeEvent([3]label.Label{
|
|
||||||
keys.Msg.Of(message),
|
|
||||||
t1,
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log2 takes a message and two labels and delivers a log event to the exporter.
|
|
||||||
// It is a customized version of Print that is faster and does no allocation.
|
|
||||||
func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) {
|
|
||||||
Export(ctx, MakeEvent([3]label.Label{
|
|
||||||
keys.Msg.Of(message),
|
|
||||||
t1,
|
|
||||||
t2,
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metric1 sends a label event to the exporter with the supplied labels.
|
|
||||||
func Metric1(ctx context.Context, t1 label.Label) context.Context {
|
|
||||||
return Export(ctx, MakeEvent([3]label.Label{
|
|
||||||
keys.Metric.New(),
|
|
||||||
t1,
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metric2 sends a label event to the exporter with the supplied labels.
|
|
||||||
func Metric2(ctx context.Context, t1, t2 label.Label) context.Context {
|
|
||||||
return Export(ctx, MakeEvent([3]label.Label{
|
|
||||||
keys.Metric.New(),
|
|
||||||
t1,
|
|
||||||
t2,
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start1 sends a span start event with the supplied label list to the exporter.
|
|
||||||
// It also returns a function that will end the span, which should normally be
|
|
||||||
// deferred.
|
|
||||||
func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) {
|
|
||||||
return ExportPair(ctx,
|
|
||||||
MakeEvent([3]label.Label{
|
|
||||||
keys.Start.Of(name),
|
|
||||||
t1,
|
|
||||||
}, nil),
|
|
||||||
MakeEvent([3]label.Label{
|
|
||||||
keys.End.New(),
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start2 sends a span start event with the supplied label list to the exporter.
|
|
||||||
// It also returns a function that will end the span, which should normally be
|
|
||||||
// deferred.
|
|
||||||
func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) {
|
|
||||||
return ExportPair(ctx,
|
|
||||||
MakeEvent([3]label.Label{
|
|
||||||
keys.Start.Of(name),
|
|
||||||
t1,
|
|
||||||
t2,
|
|
||||||
}, nil),
|
|
||||||
MakeEvent([3]label.Label{
|
|
||||||
keys.End.New(),
|
|
||||||
}, nil))
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// 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 event provides a set of packages that cover the main
|
|
||||||
// concepts of telemetry in an implementation agnostic way.
|
|
||||||
package event
|
|
|
@ -1,127 +0,0 @@
|
||||||
// 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 event
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/core"
|
|
||||||
"golang.org/x/tools/internal/event/keys"
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Exporter is a function that handles events.
|
|
||||||
// It may return a modified context and event.
|
|
||||||
type Exporter func(context.Context, core.Event, label.Map) context.Context
|
|
||||||
|
|
||||||
// SetExporter sets the global exporter function that handles all events.
|
|
||||||
// The exporter is called synchronously from the event call site, so it should
|
|
||||||
// return quickly so as not to hold up user code.
|
|
||||||
func SetExporter(e Exporter) {
|
|
||||||
core.SetExporter(core.Exporter(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log takes a message and a label list and combines them into a single event
|
|
||||||
// before delivering them to the exporter.
|
|
||||||
func Log(ctx context.Context, message string, labels ...label.Label) {
|
|
||||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Msg.Of(message),
|
|
||||||
}, labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLog returns true if the event was built by the Log function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsLog(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Msg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error takes a message and a label list and combines them into a single event
|
|
||||||
// before delivering them to the exporter. It captures the error in the
|
|
||||||
// delivered event.
|
|
||||||
func Error(ctx context.Context, message string, err error, labels ...label.Label) {
|
|
||||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Msg.Of(message),
|
|
||||||
keys.Err.Of(err),
|
|
||||||
}, labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsError returns true if the event was built by the Error function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsError(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Msg &&
|
|
||||||
ev.Label(1).Key() == keys.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metric sends a label event to the exporter with the supplied labels.
|
|
||||||
func Metric(ctx context.Context, labels ...label.Label) {
|
|
||||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Metric.New(),
|
|
||||||
}, labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsMetric returns true if the event was built by the Metric function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsMetric(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Metric
|
|
||||||
}
|
|
||||||
|
|
||||||
// Label sends a label event to the exporter with the supplied labels.
|
|
||||||
func Label(ctx context.Context, labels ...label.Label) context.Context {
|
|
||||||
return core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Label.New(),
|
|
||||||
}, labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLabel returns true if the event was built by the Label function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsLabel(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Label
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start sends a span start event with the supplied label list to the exporter.
|
|
||||||
// It also returns a function that will end the span, which should normally be
|
|
||||||
// deferred.
|
|
||||||
func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) {
|
|
||||||
return core.ExportPair(ctx,
|
|
||||||
core.MakeEvent([3]label.Label{
|
|
||||||
keys.Start.Of(name),
|
|
||||||
}, labels),
|
|
||||||
core.MakeEvent([3]label.Label{
|
|
||||||
keys.End.New(),
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsStart returns true if the event was built by the Start function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsStart(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Start
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEnd returns true if the event was built by the End function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsEnd(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.End
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detach returns a context without an associated span.
|
|
||||||
// This allows the creation of spans that are not children of the current span.
|
|
||||||
func Detach(ctx context.Context) context.Context {
|
|
||||||
return core.Export(ctx, core.MakeEvent([3]label.Label{
|
|
||||||
keys.Detach.New(),
|
|
||||||
}, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDetach returns true if the event was built by the Detach function.
|
|
||||||
// It is intended to be used in exporters to identify the semantics of the
|
|
||||||
// event when deciding what to do with it.
|
|
||||||
func IsDetach(ev core.Event) bool {
|
|
||||||
return ev.Label(0).Key() == keys.Detach
|
|
||||||
}
|
|
|
@ -1,564 +0,0 @@
|
||||||
// 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 keys
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/event/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Value represents a key for untyped values.
|
|
||||||
type Value struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new Key for untyped values.
|
|
||||||
func New(name, description string) *Value {
|
|
||||||
return &Value{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Value) Name() string { return k.name }
|
|
||||||
func (k *Value) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Value) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
fmt.Fprint(w, k.From(l))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Value) Get(lm label.Map) interface{} {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Value) From(t label.Label) interface{} { return t.UnpackValue() }
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Value) Of(value interface{}) label.Label { return label.OfValue(k, value) }
|
|
||||||
|
|
||||||
// Tag represents a key for tagging labels that have no value.
|
|
||||||
// These are used when the existence of the label is the entire information it
|
|
||||||
// carries, such as marking events to be of a specific kind, or from a specific
|
|
||||||
// package.
|
|
||||||
type Tag struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTag creates a new Key for tagging labels.
|
|
||||||
func NewTag(name, description string) *Tag {
|
|
||||||
return &Tag{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Tag) Name() string { return k.name }
|
|
||||||
func (k *Tag) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {}
|
|
||||||
|
|
||||||
// New creates a new Label with this key.
|
|
||||||
func (k *Tag) New() label.Label { return label.OfValue(k, nil) }
|
|
||||||
|
|
||||||
// Int represents a key
|
|
||||||
type Int struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInt creates a new Key for int values.
|
|
||||||
func NewInt(name, description string) *Int {
|
|
||||||
return &Int{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Int) Name() string { return k.name }
|
|
||||||
func (k *Int) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Int) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Int) Get(lm label.Map) int {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Int) From(t label.Label) int { return int(t.Unpack64()) }
|
|
||||||
|
|
||||||
// Int8 represents a key
|
|
||||||
type Int8 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInt8 creates a new Key for int8 values.
|
|
||||||
func NewInt8(name, description string) *Int8 {
|
|
||||||
return &Int8{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Int8) Name() string { return k.name }
|
|
||||||
func (k *Int8) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Int8) Get(lm label.Map) int8 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) }
|
|
||||||
|
|
||||||
// Int16 represents a key
|
|
||||||
type Int16 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInt16 creates a new Key for int16 values.
|
|
||||||
func NewInt16(name, description string) *Int16 {
|
|
||||||
return &Int16{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Int16) Name() string { return k.name }
|
|
||||||
func (k *Int16) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Int16) Get(lm label.Map) int16 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) }
|
|
||||||
|
|
||||||
// Int32 represents a key
|
|
||||||
type Int32 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInt32 creates a new Key for int32 values.
|
|
||||||
func NewInt32(name, description string) *Int32 {
|
|
||||||
return &Int32{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Int32) Name() string { return k.name }
|
|
||||||
func (k *Int32) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Int32) Get(lm label.Map) int32 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) }
|
|
||||||
|
|
||||||
// Int64 represents a key
|
|
||||||
type Int64 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInt64 creates a new Key for int64 values.
|
|
||||||
func NewInt64(name, description string) *Int64 {
|
|
||||||
return &Int64{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Int64) Name() string { return k.name }
|
|
||||||
func (k *Int64) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendInt(buf, k.From(l), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Int64) Get(lm label.Map) int64 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) }
|
|
||||||
|
|
||||||
// UInt represents a key
|
|
||||||
type UInt struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUInt creates a new Key for uint values.
|
|
||||||
func NewUInt(name, description string) *UInt {
|
|
||||||
return &UInt{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *UInt) Name() string { return k.name }
|
|
||||||
func (k *UInt) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *UInt) Get(lm label.Map) uint {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) }
|
|
||||||
|
|
||||||
// UInt8 represents a key
|
|
||||||
type UInt8 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUInt8 creates a new Key for uint8 values.
|
|
||||||
func NewUInt8(name, description string) *UInt8 {
|
|
||||||
return &UInt8{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *UInt8) Name() string { return k.name }
|
|
||||||
func (k *UInt8) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *UInt8) Get(lm label.Map) uint8 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) }
|
|
||||||
|
|
||||||
// UInt16 represents a key
|
|
||||||
type UInt16 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUInt16 creates a new Key for uint16 values.
|
|
||||||
func NewUInt16(name, description string) *UInt16 {
|
|
||||||
return &UInt16{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *UInt16) Name() string { return k.name }
|
|
||||||
func (k *UInt16) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *UInt16) Get(lm label.Map) uint16 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) }
|
|
||||||
|
|
||||||
// UInt32 represents a key
|
|
||||||
type UInt32 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUInt32 creates a new Key for uint32 values.
|
|
||||||
func NewUInt32(name, description string) *UInt32 {
|
|
||||||
return &UInt32{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *UInt32) Name() string { return k.name }
|
|
||||||
func (k *UInt32) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *UInt32) Get(lm label.Map) uint32 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) }
|
|
||||||
|
|
||||||
// UInt64 represents a key
|
|
||||||
type UInt64 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUInt64 creates a new Key for uint64 values.
|
|
||||||
func NewUInt64(name, description string) *UInt64 {
|
|
||||||
return &UInt64{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *UInt64) Name() string { return k.name }
|
|
||||||
func (k *UInt64) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendUint(buf, k.From(l), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *UInt64) Get(lm label.Map) uint64 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() }
|
|
||||||
|
|
||||||
// Float32 represents a key
|
|
||||||
type Float32 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFloat32 creates a new Key for float32 values.
|
|
||||||
func NewFloat32(name, description string) *Float32 {
|
|
||||||
return &Float32{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Float32) Name() string { return k.name }
|
|
||||||
func (k *Float32) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Float32) Of(v float32) label.Label {
|
|
||||||
return label.Of64(k, uint64(math.Float32bits(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Float32) Get(lm label.Map) float32 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Float32) From(t label.Label) float32 {
|
|
||||||
return math.Float32frombits(uint32(t.Unpack64()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64 represents a key
|
|
||||||
type Float64 struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFloat64 creates a new Key for int64 values.
|
|
||||||
func NewFloat64(name, description string) *Float64 {
|
|
||||||
return &Float64{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Float64) Name() string { return k.name }
|
|
||||||
func (k *Float64) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Float64) Of(v float64) label.Label {
|
|
||||||
return label.Of64(k, math.Float64bits(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Float64) Get(lm label.Map) float64 {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Float64) From(t label.Label) float64 {
|
|
||||||
return math.Float64frombits(t.Unpack64())
|
|
||||||
}
|
|
||||||
|
|
||||||
// String represents a key
|
|
||||||
type String struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewString creates a new Key for int64 values.
|
|
||||||
func NewString(name, description string) *String {
|
|
||||||
return &String{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *String) Name() string { return k.name }
|
|
||||||
func (k *String) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *String) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendQuote(buf, k.From(l)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *String) Of(v string) label.Label { return label.OfString(k, v) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *String) Get(lm label.Map) string {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *String) From(t label.Label) string { return t.UnpackString() }
|
|
||||||
|
|
||||||
// Boolean represents a key
|
|
||||||
type Boolean struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBoolean creates a new Key for bool values.
|
|
||||||
func NewBoolean(name, description string) *Boolean {
|
|
||||||
return &Boolean{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Boolean) Name() string { return k.name }
|
|
||||||
func (k *Boolean) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
w.Write(strconv.AppendBool(buf, k.From(l)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Boolean) Of(v bool) label.Label {
|
|
||||||
if v {
|
|
||||||
return label.Of64(k, 1)
|
|
||||||
}
|
|
||||||
return label.Of64(k, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Boolean) Get(lm label.Map) bool {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 }
|
|
||||||
|
|
||||||
// Error represents a key
|
|
||||||
type Error struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewError creates a new Key for int64 values.
|
|
||||||
func NewError(name, description string) *Error {
|
|
||||||
return &Error{name: name, description: description}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Error) Name() string { return k.name }
|
|
||||||
func (k *Error) Description() string { return k.description }
|
|
||||||
|
|
||||||
func (k *Error) Format(w io.Writer, buf []byte, l label.Label) {
|
|
||||||
io.WriteString(w, k.From(l).Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a new Label with this key and the supplied value.
|
|
||||||
func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) }
|
|
||||||
|
|
||||||
// Get can be used to get a label for the key from a label.Map.
|
|
||||||
func (k *Error) Get(lm label.Map) error {
|
|
||||||
if t := lm.Find(k); t.Valid() {
|
|
||||||
return k.From(t)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// From can be used to get a value from a Label.
|
|
||||||
func (k *Error) From(t label.Label) error {
|
|
||||||
err, _ := t.UnpackValue().(error)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2020 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 keys
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Msg is a key used to add message strings to label lists.
|
|
||||||
Msg = NewString("message", "a readable message")
|
|
||||||
// Label is a key used to indicate an event adds labels to the context.
|
|
||||||
Label = NewTag("label", "a label context marker")
|
|
||||||
// Start is used for things like traces that have a name.
|
|
||||||
Start = NewString("start", "span start")
|
|
||||||
// Metric is a key used to indicate an event records metrics.
|
|
||||||
End = NewTag("end", "a span end marker")
|
|
||||||
// Metric is a key used to indicate an event records metrics.
|
|
||||||
Detach = NewTag("detach", "a span detach marker")
|
|
||||||
// Err is a key used to add error values to label lists.
|
|
||||||
Err = NewError("error", "an error that occurred")
|
|
||||||
// Metric is a key used to indicate an event records metrics.
|
|
||||||
Metric = NewTag("metric", "a metric event marker")
|
|
||||||
)
|
|
|
@ -1,215 +0,0 @@
|
||||||
// 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 label
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Key is used as the identity of a Label.
|
|
||||||
// Keys are intended to be compared by pointer only, the name should be unique
|
|
||||||
// for communicating with external systems, but it is not required or enforced.
|
|
||||||
type Key interface {
|
|
||||||
// Name returns the key name.
|
|
||||||
Name() string
|
|
||||||
// Description returns a string that can be used to describe the value.
|
|
||||||
Description() string
|
|
||||||
|
|
||||||
// Format is used in formatting to append the value of the label to the
|
|
||||||
// supplied buffer.
|
|
||||||
// The formatter may use the supplied buf as a scratch area to avoid
|
|
||||||
// allocations.
|
|
||||||
Format(w io.Writer, buf []byte, l Label)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Label holds a key and value pair.
|
|
||||||
// It is normally used when passing around lists of labels.
|
|
||||||
type Label struct {
|
|
||||||
key Key
|
|
||||||
packed uint64
|
|
||||||
untyped interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map is the interface to a collection of Labels indexed by key.
|
|
||||||
type Map interface {
|
|
||||||
// Find returns the label that matches the supplied key.
|
|
||||||
Find(key Key) Label
|
|
||||||
}
|
|
||||||
|
|
||||||
// List is the interface to something that provides an iterable
|
|
||||||
// list of labels.
|
|
||||||
// Iteration should start from 0 and continue until Valid returns false.
|
|
||||||
type List interface {
|
|
||||||
// Valid returns true if the index is within range for the list.
|
|
||||||
// It does not imply the label at that index will itself be valid.
|
|
||||||
Valid(index int) bool
|
|
||||||
// Label returns the label at the given index.
|
|
||||||
Label(index int) Label
|
|
||||||
}
|
|
||||||
|
|
||||||
// list implements LabelList for a list of Labels.
|
|
||||||
type list struct {
|
|
||||||
labels []Label
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter wraps a LabelList filtering out specific labels.
|
|
||||||
type filter struct {
|
|
||||||
keys []Key
|
|
||||||
underlying List
|
|
||||||
}
|
|
||||||
|
|
||||||
// listMap implements LabelMap for a simple list of labels.
|
|
||||||
type listMap struct {
|
|
||||||
labels []Label
|
|
||||||
}
|
|
||||||
|
|
||||||
// mapChain implements LabelMap for a list of underlying LabelMap.
|
|
||||||
type mapChain struct {
|
|
||||||
maps []Map
|
|
||||||
}
|
|
||||||
|
|
||||||
// OfValue creates a new label from the key and value.
|
|
||||||
// This method is for implementing new key types, label creation should
|
|
||||||
// normally be done with the Of method of the key.
|
|
||||||
func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} }
|
|
||||||
|
|
||||||
// UnpackValue assumes the label was built using LabelOfValue and returns the value
|
|
||||||
// that was passed to that constructor.
|
|
||||||
// This method is for implementing new key types, for type safety normal
|
|
||||||
// access should be done with the From method of the key.
|
|
||||||
func (t Label) UnpackValue() interface{} { return t.untyped }
|
|
||||||
|
|
||||||
// Of64 creates a new label from a key and a uint64. This is often
|
|
||||||
// used for non uint64 values that can be packed into a uint64.
|
|
||||||
// This method is for implementing new key types, label creation should
|
|
||||||
// normally be done with the Of method of the key.
|
|
||||||
func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} }
|
|
||||||
|
|
||||||
// Unpack64 assumes the label was built using LabelOf64 and returns the value that
|
|
||||||
// was passed to that constructor.
|
|
||||||
// This method is for implementing new key types, for type safety normal
|
|
||||||
// access should be done with the From method of the key.
|
|
||||||
func (t Label) Unpack64() uint64 { return t.packed }
|
|
||||||
|
|
||||||
type stringptr unsafe.Pointer
|
|
||||||
|
|
||||||
// OfString creates a new label from a key and a string.
|
|
||||||
// This method is for implementing new key types, label creation should
|
|
||||||
// normally be done with the Of method of the key.
|
|
||||||
func OfString(k Key, v string) Label {
|
|
||||||
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
|
|
||||||
return Label{
|
|
||||||
key: k,
|
|
||||||
packed: uint64(hdr.Len),
|
|
||||||
untyped: stringptr(hdr.Data),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackString assumes the label was built using LabelOfString and returns the
|
|
||||||
// value that was passed to that constructor.
|
|
||||||
// This method is for implementing new key types, for type safety normal
|
|
||||||
// access should be done with the From method of the key.
|
|
||||||
func (t Label) UnpackString() string {
|
|
||||||
var v string
|
|
||||||
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
|
|
||||||
hdr.Data = uintptr(t.untyped.(stringptr))
|
|
||||||
hdr.Len = int(t.packed)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid returns true if the Label is a valid one (it has a key).
|
|
||||||
func (t Label) Valid() bool { return t.key != nil }
|
|
||||||
|
|
||||||
// Key returns the key of this Label.
|
|
||||||
func (t Label) Key() Key { return t.key }
|
|
||||||
|
|
||||||
// Format is used for debug printing of labels.
|
|
||||||
func (t Label) Format(f fmt.State, r rune) {
|
|
||||||
if !t.Valid() {
|
|
||||||
io.WriteString(f, `nil`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
io.WriteString(f, t.Key().Name())
|
|
||||||
io.WriteString(f, "=")
|
|
||||||
var buf [128]byte
|
|
||||||
t.Key().Format(f, buf[:0], t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *list) Valid(index int) bool {
|
|
||||||
return index >= 0 && index < len(l.labels)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *list) Label(index int) Label {
|
|
||||||
return l.labels[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *filter) Valid(index int) bool {
|
|
||||||
return f.underlying.Valid(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *filter) Label(index int) Label {
|
|
||||||
l := f.underlying.Label(index)
|
|
||||||
for _, f := range f.keys {
|
|
||||||
if l.Key() == f {
|
|
||||||
return Label{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lm listMap) Find(key Key) Label {
|
|
||||||
for _, l := range lm.labels {
|
|
||||||
if l.Key() == key {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Label{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c mapChain) Find(key Key) Label {
|
|
||||||
for _, src := range c.maps {
|
|
||||||
l := src.Find(key)
|
|
||||||
if l.Valid() {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Label{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var emptyList = &list{}
|
|
||||||
|
|
||||||
func NewList(labels ...Label) List {
|
|
||||||
if len(labels) == 0 {
|
|
||||||
return emptyList
|
|
||||||
}
|
|
||||||
return &list{labels: labels}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Filter(l List, keys ...Key) List {
|
|
||||||
if len(keys) == 0 {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
return &filter{keys: keys, underlying: l}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMap(labels ...Label) Map {
|
|
||||||
return listMap{labels: labels}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeMaps(srcs ...Map) Map {
|
|
||||||
var nonNil []Map
|
|
||||||
for _, src := range srcs {
|
|
||||||
if src != nil {
|
|
||||||
nonNil = append(nonNil, src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(nonNil) == 1 {
|
|
||||||
return nonNil[0]
|
|
||||||
}
|
|
||||||
return mapChain{maps: nonNil}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
// 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,150 +0,0 @@
|
||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file contains the remaining vestiges of
|
|
||||||
// $GOROOT/src/go/internal/gcimporter/bimport.go.
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
func errorf(format string, args ...interface{}) {
|
|
||||||
panic(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
|
|
||||||
|
|
||||||
// Synthesize a token.Pos
|
|
||||||
type fakeFileSet struct {
|
|
||||||
fset *token.FileSet
|
|
||||||
files map[string]*fileInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type fileInfo struct {
|
|
||||||
file *token.File
|
|
||||||
lastline int
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxlines = 64 * 1024
|
|
||||||
|
|
||||||
func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
|
|
||||||
// TODO(mdempsky): Make use of column.
|
|
||||||
|
|
||||||
// Since we don't know the set of needed file positions, we reserve maxlines
|
|
||||||
// positions per file. We delay calling token.File.SetLines until all
|
|
||||||
// positions have been calculated (by way of fakeFileSet.setLines), so that
|
|
||||||
// we can avoid setting unnecessary lines. See also golang/go#46586.
|
|
||||||
f := s.files[file]
|
|
||||||
if f == nil {
|
|
||||||
f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)}
|
|
||||||
s.files[file] = f
|
|
||||||
}
|
|
||||||
if line > maxlines {
|
|
||||||
line = 1
|
|
||||||
}
|
|
||||||
if line > f.lastline {
|
|
||||||
f.lastline = line
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a fake position assuming that f.file consists only of newlines.
|
|
||||||
return token.Pos(f.file.Base() + line - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *fakeFileSet) setLines() {
|
|
||||||
fakeLinesOnce.Do(func() {
|
|
||||||
fakeLines = make([]int, maxlines)
|
|
||||||
for i := range fakeLines {
|
|
||||||
fakeLines[i] = i
|
|
||||||
}
|
|
||||||
})
|
|
||||||
for _, f := range s.files {
|
|
||||||
f.file.SetLines(fakeLines[:f.lastline])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
fakeLines []int
|
|
||||||
fakeLinesOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
func chanDir(d int) types.ChanDir {
|
|
||||||
// tag values must match the constants in cmd/compile/internal/gc/go.go
|
|
||||||
switch d {
|
|
||||||
case 1 /* Crecv */ :
|
|
||||||
return types.RecvOnly
|
|
||||||
case 2 /* Csend */ :
|
|
||||||
return types.SendOnly
|
|
||||||
case 3 /* Cboth */ :
|
|
||||||
return types.SendRecv
|
|
||||||
default:
|
|
||||||
errorf("unexpected channel dir %d", d)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var predeclOnce sync.Once
|
|
||||||
var predecl []types.Type // initialized lazily
|
|
||||||
|
|
||||||
func predeclared() []types.Type {
|
|
||||||
predeclOnce.Do(func() {
|
|
||||||
// initialize lazily to be sure that all
|
|
||||||
// elements have been initialized before
|
|
||||||
predecl = []types.Type{ // basic types
|
|
||||||
types.Typ[types.Bool],
|
|
||||||
types.Typ[types.Int],
|
|
||||||
types.Typ[types.Int8],
|
|
||||||
types.Typ[types.Int16],
|
|
||||||
types.Typ[types.Int32],
|
|
||||||
types.Typ[types.Int64],
|
|
||||||
types.Typ[types.Uint],
|
|
||||||
types.Typ[types.Uint8],
|
|
||||||
types.Typ[types.Uint16],
|
|
||||||
types.Typ[types.Uint32],
|
|
||||||
types.Typ[types.Uint64],
|
|
||||||
types.Typ[types.Uintptr],
|
|
||||||
types.Typ[types.Float32],
|
|
||||||
types.Typ[types.Float64],
|
|
||||||
types.Typ[types.Complex64],
|
|
||||||
types.Typ[types.Complex128],
|
|
||||||
types.Typ[types.String],
|
|
||||||
|
|
||||||
// basic type aliases
|
|
||||||
types.Universe.Lookup("byte").Type(),
|
|
||||||
types.Universe.Lookup("rune").Type(),
|
|
||||||
|
|
||||||
// error
|
|
||||||
types.Universe.Lookup("error").Type(),
|
|
||||||
|
|
||||||
// untyped types
|
|
||||||
types.Typ[types.UntypedBool],
|
|
||||||
types.Typ[types.UntypedInt],
|
|
||||||
types.Typ[types.UntypedRune],
|
|
||||||
types.Typ[types.UntypedFloat],
|
|
||||||
types.Typ[types.UntypedComplex],
|
|
||||||
types.Typ[types.UntypedString],
|
|
||||||
types.Typ[types.UntypedNil],
|
|
||||||
|
|
||||||
// package unsafe
|
|
||||||
types.Typ[types.UnsafePointer],
|
|
||||||
|
|
||||||
// invalid type
|
|
||||||
types.Typ[types.Invalid], // only appears in packages with errors
|
|
||||||
|
|
||||||
// used internally by gc; never used by this package or in .a files
|
|
||||||
anyType{},
|
|
||||||
}
|
|
||||||
predecl = append(predecl, additionalPredeclared()...)
|
|
||||||
})
|
|
||||||
return predecl
|
|
||||||
}
|
|
||||||
|
|
||||||
type anyType struct{}
|
|
||||||
|
|
||||||
func (t anyType) Underlying() types.Type { return t }
|
|
||||||
func (t anyType) String() string { return "any" }
|
|
|
@ -1,99 +0,0 @@
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file is a copy of $GOROOT/src/go/internal/gcimporter/exportdata.go.
|
|
||||||
|
|
||||||
// This file implements FindExportData.
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func readGopackHeader(r *bufio.Reader) (name string, size int64, err error) {
|
|
||||||
// See $GOROOT/include/ar.h.
|
|
||||||
hdr := make([]byte, 16+12+6+6+8+10+2)
|
|
||||||
_, err = io.ReadFull(r, hdr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// leave for debugging
|
|
||||||
if false {
|
|
||||||
fmt.Printf("header: %s", hdr)
|
|
||||||
}
|
|
||||||
s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
|
|
||||||
length, err := strconv.Atoi(s)
|
|
||||||
size = int64(length)
|
|
||||||
if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
|
|
||||||
err = fmt.Errorf("invalid archive header")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name = strings.TrimSpace(string(hdr[:16]))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindExportData positions the reader r at the beginning of the
|
|
||||||
// export data section of an underlying GC-created object/archive
|
|
||||||
// file by reading from it. The reader must be positioned at the
|
|
||||||
// start of the file before calling this function. The hdr result
|
|
||||||
// is the string before the export data, either "$$" or "$$B".
|
|
||||||
// The size result is the length of the export data in bytes, or -1 if not known.
|
|
||||||
func FindExportData(r *bufio.Reader) (hdr string, size int64, err error) {
|
|
||||||
// Read first line to make sure this is an object file.
|
|
||||||
line, err := r.ReadSlice('\n')
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("can't find export data (%v)", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(line) == "!<arch>\n" {
|
|
||||||
// Archive file. Scan to __.PKGDEF.
|
|
||||||
var name string
|
|
||||||
if name, size, err = readGopackHeader(r); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// First entry should be __.PKGDEF.
|
|
||||||
if name != "__.PKGDEF" {
|
|
||||||
err = fmt.Errorf("go archive is missing __.PKGDEF")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read first line of __.PKGDEF data, so that line
|
|
||||||
// is once again the first line of the input.
|
|
||||||
if line, err = r.ReadSlice('\n'); err != nil {
|
|
||||||
err = fmt.Errorf("can't find export data (%v)", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
size -= int64(len(line))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now at __.PKGDEF in archive or still at beginning of file.
|
|
||||||
// Either way, line should begin with "go object ".
|
|
||||||
if !strings.HasPrefix(string(line), "go object ") {
|
|
||||||
err = fmt.Errorf("not a Go object file")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip over object header to export data.
|
|
||||||
// Begins after first line starting with $$.
|
|
||||||
for line[0] != '$' {
|
|
||||||
if line, err = r.ReadSlice('\n'); err != nil {
|
|
||||||
err = fmt.Errorf("can't find export data (%v)", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
size -= int64(len(line))
|
|
||||||
}
|
|
||||||
hdr = string(line)
|
|
||||||
if size < 0 {
|
|
||||||
size = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,273 +0,0 @@
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file is a reduced copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go.
|
|
||||||
|
|
||||||
// Package gcimporter provides various functions for reading
|
|
||||||
// gc-generated object files that can be used to implement the
|
|
||||||
// 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"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"go/build"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Enable debug during development: it adds some additional checks, and
|
|
||||||
// prevents errors from being recovered.
|
|
||||||
debug = false
|
|
||||||
|
|
||||||
// If trace is set, debugging output is printed to std out.
|
|
||||||
trace = false
|
|
||||||
)
|
|
||||||
|
|
||||||
var exportMap sync.Map // package dir → func() (string, bool)
|
|
||||||
|
|
||||||
// lookupGorootExport returns the location of the export data
|
|
||||||
// (normally found in the build cache, but located in GOROOT/pkg
|
|
||||||
// in prior Go releases) for the package located in pkgDir.
|
|
||||||
//
|
|
||||||
// (We use the package's directory instead of its import path
|
|
||||||
// mainly to simplify handling of the packages in src/vendor
|
|
||||||
// and cmd/vendor.)
|
|
||||||
func lookupGorootExport(pkgDir string) (string, bool) {
|
|
||||||
f, ok := exportMap.Load(pkgDir)
|
|
||||||
if !ok {
|
|
||||||
var (
|
|
||||||
listOnce sync.Once
|
|
||||||
exportPath string
|
|
||||||
)
|
|
||||||
f, _ = exportMap.LoadOrStore(pkgDir, func() (string, bool) {
|
|
||||||
listOnce.Do(func() {
|
|
||||||
cmd := exec.Command("go", "list", "-export", "-f", "{{.Export}}", pkgDir)
|
|
||||||
cmd.Dir = build.Default.GOROOT
|
|
||||||
var output []byte
|
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
exports := strings.Split(string(bytes.TrimSpace(output)), "\n")
|
|
||||||
if len(exports) != 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
exportPath = exports[0]
|
|
||||||
})
|
|
||||||
|
|
||||||
return exportPath, exportPath != ""
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.(func() (string, bool))()
|
|
||||||
}
|
|
||||||
|
|
||||||
var pkgExts = [...]string{".a", ".o"}
|
|
||||||
|
|
||||||
// FindPkg returns the filename and unique package id for an import
|
|
||||||
// path based on package information provided by build.Import (using
|
|
||||||
// the build.Default build.Context). A relative srcDir is interpreted
|
|
||||||
// relative to the current working directory.
|
|
||||||
// If no file was found, an empty filename is returned.
|
|
||||||
func FindPkg(path, srcDir string) (filename, id string) {
|
|
||||||
if path == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var noext string
|
|
||||||
switch {
|
|
||||||
default:
|
|
||||||
// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
|
|
||||||
// Don't require the source files to be present.
|
|
||||||
if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
|
|
||||||
srcDir = abs
|
|
||||||
}
|
|
||||||
bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
|
|
||||||
if bp.PkgObj == "" {
|
|
||||||
var ok bool
|
|
||||||
if bp.Goroot && bp.Dir != "" {
|
|
||||||
filename, ok = lookupGorootExport(bp.Dir)
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
id = path // make sure we have an id to print in error message
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
noext = strings.TrimSuffix(bp.PkgObj, ".a")
|
|
||||||
id = bp.ImportPath
|
|
||||||
}
|
|
||||||
|
|
||||||
case build.IsLocalImport(path):
|
|
||||||
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
|
|
||||||
noext = filepath.Join(srcDir, path)
|
|
||||||
id = noext
|
|
||||||
|
|
||||||
case filepath.IsAbs(path):
|
|
||||||
// for completeness only - go/build.Import
|
|
||||||
// does not support absolute imports
|
|
||||||
// "/x" -> "/x.ext", "/x"
|
|
||||||
noext = path
|
|
||||||
id = path
|
|
||||||
}
|
|
||||||
|
|
||||||
if false { // for debugging
|
|
||||||
if path != id {
|
|
||||||
fmt.Printf("%s -> %s\n", path, id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if filename != "" {
|
|
||||||
if f, err := os.Stat(filename); err == nil && !f.IsDir() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try extensions
|
|
||||||
for _, ext := range pkgExts {
|
|
||||||
filename = noext + ext
|
|
||||||
if f, err := os.Stat(filename); err == nil && !f.IsDir() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filename = "" // not found
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import imports a gc-generated package given its import path and srcDir, adds
|
|
||||||
// the corresponding package object to the packages map, and returns the object.
|
|
||||||
// The packages map must contain all packages already imported.
|
|
||||||
func Import(packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
|
|
||||||
var rc io.ReadCloser
|
|
||||||
var filename, id string
|
|
||||||
if lookup != nil {
|
|
||||||
// With custom lookup specified, assume that caller has
|
|
||||||
// converted path to a canonical import path for use in the map.
|
|
||||||
if path == "unsafe" {
|
|
||||||
return types.Unsafe, nil
|
|
||||||
}
|
|
||||||
id = path
|
|
||||||
|
|
||||||
// No need to re-import if the package was imported completely before.
|
|
||||||
if pkg = packages[id]; pkg != nil && pkg.Complete() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f, err := lookup(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rc = f
|
|
||||||
} else {
|
|
||||||
filename, id = FindPkg(path, srcDir)
|
|
||||||
if filename == "" {
|
|
||||||
if path == "unsafe" {
|
|
||||||
return types.Unsafe, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("can't find import: %q", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// no need to re-import if the package was imported completely before
|
|
||||||
if pkg = packages[id]; pkg != nil && pkg.Complete() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// open file
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
// add file name to error
|
|
||||||
err = fmt.Errorf("%s: %v", filename, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
rc = f
|
|
||||||
}
|
|
||||||
defer rc.Close()
|
|
||||||
|
|
||||||
var hdr string
|
|
||||||
var size int64
|
|
||||||
buf := bufio.NewReader(rc)
|
|
||||||
if hdr, size, err = FindExportData(buf); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch hdr {
|
|
||||||
case "$$B\n":
|
|
||||||
var data []byte
|
|
||||||
data, err = io.ReadAll(buf)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(gri): allow clients of go/importer to provide a FileSet.
|
|
||||||
// Or, define a new standard go/types/gcexportdata package.
|
|
||||||
fset := token.NewFileSet()
|
|
||||||
|
|
||||||
// Select appropriate importer.
|
|
||||||
if len(data) > 0 {
|
|
||||||
switch data[0] {
|
|
||||||
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)
|
|
||||||
return pkg, err
|
|
||||||
|
|
||||||
case 'u': // unified, from go1.20
|
|
||||||
_, pkg, err := UImportData(fset, packages, data[1:size], id)
|
|
||||||
return pkg, err
|
|
||||||
|
|
||||||
default:
|
|
||||||
l := len(data)
|
|
||||||
if l > 10 {
|
|
||||||
l = 10
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("unknown export data header: %q", hdr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func deref(typ types.Type) types.Type {
|
|
||||||
if p, _ := typ.(*types.Pointer); p != nil {
|
|
||||||
return p.Elem()
|
|
||||||
}
|
|
||||||
return typ
|
|
||||||
}
|
|
||||||
|
|
||||||
type byPath []*types.Package
|
|
||||||
|
|
||||||
func (a byPath) Len() int { return len(a) }
|
|
||||||
func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,22 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
//go:build !go1.11
|
|
||||||
// +build !go1.11
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import "go/types"
|
|
||||||
|
|
||||||
func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
|
|
||||||
named := make([]*types.Named, len(embeddeds))
|
|
||||||
for i, e := range embeddeds {
|
|
||||||
var ok bool
|
|
||||||
named[i], ok = e.(*types.Named)
|
|
||||||
if !ok {
|
|
||||||
panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types.NewInterface(methods, named)
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
//go:build go1.11
|
|
||||||
// +build go1.11
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import "go/types"
|
|
||||||
|
|
||||||
func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
|
|
||||||
return types.NewInterfaceType(methods, embeddeds)
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build !go1.18
|
|
||||||
// +build !go1.18
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import "go/types"
|
|
||||||
|
|
||||||
const iexportVersion = iexportVersionGo1_11
|
|
||||||
|
|
||||||
func additionalPredeclared() []types.Type {
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build go1.18
|
|
||||||
// +build go1.18
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import "go/types"
|
|
||||||
|
|
||||||
const iexportVersion = iexportVersionGenerics
|
|
||||||
|
|
||||||
// additionalPredeclared returns additional predeclared types in go.1.18.
|
|
||||||
func additionalPredeclared() []types.Type {
|
|
||||||
return []types.Type{
|
|
||||||
// comparable
|
|
||||||
types.Universe.Lookup("comparable").Type(),
|
|
||||||
|
|
||||||
// any
|
|
||||||
types.Universe.Lookup("any").Type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See cmd/compile/internal/types.SplitVargenSuffix.
|
|
||||||
func splitVargenSuffix(name string) (base, suffix string) {
|
|
||||||
i := len(name)
|
|
||||||
for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
const dot = "·"
|
|
||||||
if i >= len(dot) && name[i-len(dot):i] == dot {
|
|
||||||
i -= len(dot)
|
|
||||||
return name[:i], name[i:]
|
|
||||||
}
|
|
||||||
return name, ""
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
// Copyright 2022 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.
|
|
||||||
|
|
||||||
//go:build !(go1.18 && goexperiment.unified)
|
|
||||||
// +build !go1.18 !goexperiment.unified
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
const unifiedIR = false
|
|
|
@ -1,10 +0,0 @@
|
||||||
// Copyright 2022 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.
|
|
||||||
|
|
||||||
//go:build go1.18 && goexperiment.unified
|
|
||||||
// +build go1.18,goexperiment.unified
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
const unifiedIR = true
|
|
|
@ -1,19 +0,0 @@
|
||||||
// Copyright 2022 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.
|
|
||||||
|
|
||||||
//go:build !go1.18
|
|
||||||
// +build !go1.18
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
|
|
||||||
err = fmt.Errorf("go/tools compiled with a Go version earlier than 1.18 cannot read unified IR export data")
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,728 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
// Derived from go/internal/gcimporter/ureader.go
|
|
||||||
|
|
||||||
//go:build go1.18
|
|
||||||
// +build go1.18
|
|
||||||
|
|
||||||
package gcimporter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/internal/pkgbits"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A pkgReader holds the shared state for reading a unified IR package
|
|
||||||
// description.
|
|
||||||
type pkgReader struct {
|
|
||||||
pkgbits.PkgDecoder
|
|
||||||
|
|
||||||
fake fakeFileSet
|
|
||||||
|
|
||||||
ctxt *types.Context
|
|
||||||
imports map[string]*types.Package // previously imported packages, indexed by path
|
|
||||||
|
|
||||||
// lazily initialized arrays corresponding to the unified IR
|
|
||||||
// PosBase, Pkg, and Type sections, respectively.
|
|
||||||
posBases []string // position bases (i.e., file names)
|
|
||||||
pkgs []*types.Package
|
|
||||||
typs []types.Type
|
|
||||||
|
|
||||||
// laterFns holds functions that need to be invoked at the end of
|
|
||||||
// import reading.
|
|
||||||
laterFns []func()
|
|
||||||
// laterFors is used in case of 'type A B' to ensure that B is processed before A.
|
|
||||||
laterFors map[types.Type]int
|
|
||||||
|
|
||||||
// ifaces holds a list of constructed Interfaces, which need to have
|
|
||||||
// Complete called after importing is done.
|
|
||||||
ifaces []*types.Interface
|
|
||||||
}
|
|
||||||
|
|
||||||
// later adds a function to be invoked at the end of import reading.
|
|
||||||
func (pr *pkgReader) later(fn func()) {
|
|
||||||
pr.laterFns = append(pr.laterFns, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// See cmd/compile/internal/noder.derivedInfo.
|
|
||||||
type derivedInfo struct {
|
|
||||||
idx pkgbits.Index
|
|
||||||
needed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// See cmd/compile/internal/noder.typeInfo.
|
|
||||||
type typeInfo struct {
|
|
||||||
idx pkgbits.Index
|
|
||||||
derived bool
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = s[:strings.LastIndex(s, "\n$$\n")]
|
|
||||||
input := pkgbits.NewPkgDecoder(path, s)
|
|
||||||
pkg = readUnifiedPackage(fset, nil, imports, input)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// laterFor adds a function to be invoked at the end of import reading, and records the type that function is finishing.
|
|
||||||
func (pr *pkgReader) laterFor(t types.Type, fn func()) {
|
|
||||||
if pr.laterFors == nil {
|
|
||||||
pr.laterFors = make(map[types.Type]int)
|
|
||||||
}
|
|
||||||
pr.laterFors[t] = len(pr.laterFns)
|
|
||||||
pr.laterFns = append(pr.laterFns, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUnifiedPackage reads a package description from the given
|
|
||||||
// unified IR export data decoder.
|
|
||||||
func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[string]*types.Package, input pkgbits.PkgDecoder) *types.Package {
|
|
||||||
pr := pkgReader{
|
|
||||||
PkgDecoder: input,
|
|
||||||
|
|
||||||
fake: fakeFileSet{
|
|
||||||
fset: fset,
|
|
||||||
files: make(map[string]*fileInfo),
|
|
||||||
},
|
|
||||||
|
|
||||||
ctxt: ctxt,
|
|
||||||
imports: imports,
|
|
||||||
|
|
||||||
posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)),
|
|
||||||
pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)),
|
|
||||||
typs: make([]types.Type, input.NumElems(pkgbits.RelocType)),
|
|
||||||
}
|
|
||||||
defer pr.fake.setLines()
|
|
||||||
|
|
||||||
r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
|
|
||||||
pkg := r.pkg()
|
|
||||||
r.Bool() // has init
|
|
||||||
|
|
||||||
for i, n := 0, r.Len(); i < n; i++ {
|
|
||||||
// As if r.obj(), but avoiding the Scope.Lookup call,
|
|
||||||
// to avoid eager loading of imports.
|
|
||||||
r.Sync(pkgbits.SyncObject)
|
|
||||||
assert(!r.Bool())
|
|
||||||
r.p.objIdx(r.Reloc(pkgbits.RelocObj))
|
|
||||||
assert(r.Len() == 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Sync(pkgbits.SyncEOF)
|
|
||||||
|
|
||||||
for _, fn := range pr.laterFns {
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, iface := range pr.ifaces {
|
|
||||||
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()
|
|
||||||
return pkg
|
|
||||||
}
|
|
||||||
|
|
||||||
// A reader holds the state for reading a single unified IR element
|
|
||||||
// within a package.
|
|
||||||
type reader struct {
|
|
||||||
pkgbits.Decoder
|
|
||||||
|
|
||||||
p *pkgReader
|
|
||||||
|
|
||||||
dict *readerDict
|
|
||||||
}
|
|
||||||
|
|
||||||
// A readerDict holds the state for type parameters that parameterize
|
|
||||||
// the current unified IR element.
|
|
||||||
type readerDict struct {
|
|
||||||
// bounds is a slice of typeInfos corresponding to the underlying
|
|
||||||
// bounds of the element's type parameters.
|
|
||||||
bounds []typeInfo
|
|
||||||
|
|
||||||
// tparams is a slice of the constructed TypeParams for the element.
|
|
||||||
tparams []*types.TypeParam
|
|
||||||
|
|
||||||
// devived is a slice of types derived from tparams, which may be
|
|
||||||
// instantiated while reading the current element.
|
|
||||||
derived []derivedInfo
|
|
||||||
derivedTypes []types.Type // lazily instantiated from derived
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
|
|
||||||
return &reader{
|
|
||||||
Decoder: pr.NewDecoder(k, idx, marker),
|
|
||||||
p: pr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *pkgReader) tempReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
|
|
||||||
return &reader{
|
|
||||||
Decoder: pr.TempDecoder(k, idx, marker),
|
|
||||||
p: pr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *pkgReader) retireReader(r *reader) {
|
|
||||||
pr.RetireDecoder(&r.Decoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @@@ Positions
|
|
||||||
|
|
||||||
func (r *reader) pos() token.Pos {
|
|
||||||
r.Sync(pkgbits.SyncPos)
|
|
||||||
if !r.Bool() {
|
|
||||||
return token.NoPos
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(mdempsky): Delta encoding.
|
|
||||||
posBase := r.posBase()
|
|
||||||
line := r.Uint()
|
|
||||||
col := r.Uint()
|
|
||||||
return r.p.fake.pos(posBase, int(line), int(col))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) posBase() string {
|
|
||||||
return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) string {
|
|
||||||
if b := pr.posBases[idx]; b != "" {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
var filename string
|
|
||||||
{
|
|
||||||
r := pr.tempReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase)
|
|
||||||
|
|
||||||
// Within types2, position bases have a lot more details (e.g.,
|
|
||||||
// keeping track of where //line directives appeared exactly).
|
|
||||||
//
|
|
||||||
// For go/types, we just track the file name.
|
|
||||||
|
|
||||||
filename = r.String()
|
|
||||||
|
|
||||||
if r.Bool() { // file base
|
|
||||||
// Was: "b = token.NewTrimmedFileBase(filename, true)"
|
|
||||||
} else { // line base
|
|
||||||
pos := r.pos()
|
|
||||||
line := r.Uint()
|
|
||||||
col := r.Uint()
|
|
||||||
|
|
||||||
// Was: "b = token.NewLineBase(pos, filename, true, line, col)"
|
|
||||||
_, _, _ = pos, line, col
|
|
||||||
}
|
|
||||||
pr.retireReader(r)
|
|
||||||
}
|
|
||||||
b := filename
|
|
||||||
pr.posBases[idx] = b
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// @@@ Packages
|
|
||||||
|
|
||||||
func (r *reader) pkg() *types.Package {
|
|
||||||
r.Sync(pkgbits.SyncPkg)
|
|
||||||
return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Package {
|
|
||||||
// TODO(mdempsky): Consider using some non-nil pointer to indicate
|
|
||||||
// the universe scope, so we don't need to keep re-reading it.
|
|
||||||
if pkg := pr.pkgs[idx]; pkg != nil {
|
|
||||||
return pkg
|
|
||||||
}
|
|
||||||
|
|
||||||
pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg()
|
|
||||||
pr.pkgs[idx] = pkg
|
|
||||||
return pkg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) doPkg() *types.Package {
|
|
||||||
path := r.String()
|
|
||||||
switch path {
|
|
||||||
case "":
|
|
||||||
path = r.p.PkgPath()
|
|
||||||
case "builtin":
|
|
||||||
return nil // universe
|
|
||||||
case "unsafe":
|
|
||||||
return types.Unsafe
|
|
||||||
}
|
|
||||||
|
|
||||||
if pkg := r.p.imports[path]; pkg != nil {
|
|
||||||
return pkg
|
|
||||||
}
|
|
||||||
|
|
||||||
name := r.String()
|
|
||||||
|
|
||||||
pkg := types.NewPackage(path, name)
|
|
||||||
r.p.imports[path] = pkg
|
|
||||||
|
|
||||||
return pkg
|
|
||||||
}
|
|
||||||
|
|
||||||
// @@@ Types
|
|
||||||
|
|
||||||
func (r *reader) typ() types.Type {
|
|
||||||
return r.p.typIdx(r.typInfo(), r.dict)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) typInfo() typeInfo {
|
|
||||||
r.Sync(pkgbits.SyncType)
|
|
||||||
if r.Bool() {
|
|
||||||
return typeInfo{idx: pkgbits.Index(r.Len()), derived: true}
|
|
||||||
}
|
|
||||||
return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types.Type {
|
|
||||||
idx := info.idx
|
|
||||||
var where *types.Type
|
|
||||||
if info.derived {
|
|
||||||
where = &dict.derivedTypes[idx]
|
|
||||||
idx = dict.derived[idx].idx
|
|
||||||
} else {
|
|
||||||
where = &pr.typs[idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
if typ := *where; typ != nil {
|
|
||||||
return typ
|
|
||||||
}
|
|
||||||
|
|
||||||
var typ types.Type
|
|
||||||
{
|
|
||||||
r := pr.tempReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx)
|
|
||||||
r.dict = dict
|
|
||||||
|
|
||||||
typ = r.doTyp()
|
|
||||||
assert(typ != nil)
|
|
||||||
pr.retireReader(r)
|
|
||||||
}
|
|
||||||
// See comment in pkgReader.typIdx explaining how this happens.
|
|
||||||
if prev := *where; prev != nil {
|
|
||||||
return prev
|
|
||||||
}
|
|
||||||
|
|
||||||
*where = typ
|
|
||||||
return typ
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) doTyp() (res types.Type) {
|
|
||||||
switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag {
|
|
||||||
default:
|
|
||||||
errorf("unhandled type tag: %v", tag)
|
|
||||||
panic("unreachable")
|
|
||||||
|
|
||||||
case pkgbits.TypeBasic:
|
|
||||||
return types.Typ[r.Len()]
|
|
||||||
|
|
||||||
case pkgbits.TypeNamed:
|
|
||||||
obj, targs := r.obj()
|
|
||||||
name := obj.(*types.TypeName)
|
|
||||||
if len(targs) != 0 {
|
|
||||||
t, _ := types.Instantiate(r.p.ctxt, name.Type(), targs, false)
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
return name.Type()
|
|
||||||
|
|
||||||
case pkgbits.TypeTypeParam:
|
|
||||||
return r.dict.tparams[r.Len()]
|
|
||||||
|
|
||||||
case pkgbits.TypeArray:
|
|
||||||
len := int64(r.Uint64())
|
|
||||||
return types.NewArray(r.typ(), len)
|
|
||||||
case pkgbits.TypeChan:
|
|
||||||
dir := types.ChanDir(r.Len())
|
|
||||||
return types.NewChan(dir, r.typ())
|
|
||||||
case pkgbits.TypeMap:
|
|
||||||
return types.NewMap(r.typ(), r.typ())
|
|
||||||
case pkgbits.TypePointer:
|
|
||||||
return types.NewPointer(r.typ())
|
|
||||||
case pkgbits.TypeSignature:
|
|
||||||
return r.signature(nil, nil, nil)
|
|
||||||
case pkgbits.TypeSlice:
|
|
||||||
return types.NewSlice(r.typ())
|
|
||||||
case pkgbits.TypeStruct:
|
|
||||||
return r.structType()
|
|
||||||
case pkgbits.TypeInterface:
|
|
||||||
return r.interfaceType()
|
|
||||||
case pkgbits.TypeUnion:
|
|
||||||
return r.unionType()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) structType() *types.Struct {
|
|
||||||
fields := make([]*types.Var, r.Len())
|
|
||||||
var tags []string
|
|
||||||
for i := range fields {
|
|
||||||
pos := r.pos()
|
|
||||||
pkg, name := r.selector()
|
|
||||||
ftyp := r.typ()
|
|
||||||
tag := r.String()
|
|
||||||
embedded := r.Bool()
|
|
||||||
|
|
||||||
fields[i] = types.NewField(pos, pkg, name, ftyp, embedded)
|
|
||||||
if tag != "" {
|
|
||||||
for len(tags) < i {
|
|
||||||
tags = append(tags, "")
|
|
||||||
}
|
|
||||||
tags = append(tags, tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types.NewStruct(fields, tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) unionType() *types.Union {
|
|
||||||
terms := make([]*types.Term, r.Len())
|
|
||||||
for i := range terms {
|
|
||||||
terms[i] = types.NewTerm(r.Bool(), r.typ())
|
|
||||||
}
|
|
||||||
return types.NewUnion(terms)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) interfaceType() *types.Interface {
|
|
||||||
methods := make([]*types.Func, r.Len())
|
|
||||||
embeddeds := make([]types.Type, r.Len())
|
|
||||||
implicit := len(methods) == 0 && len(embeddeds) == 1 && r.Bool()
|
|
||||||
|
|
||||||
for i := range methods {
|
|
||||||
pos := r.pos()
|
|
||||||
pkg, name := r.selector()
|
|
||||||
mtyp := r.signature(nil, nil, nil)
|
|
||||||
methods[i] = types.NewFunc(pos, pkg, name, mtyp)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range embeddeds {
|
|
||||||
embeddeds[i] = r.typ()
|
|
||||||
}
|
|
||||||
|
|
||||||
iface := types.NewInterfaceType(methods, embeddeds)
|
|
||||||
if implicit {
|
|
||||||
iface.MarkImplicit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to call iface.Complete(), but if there are any embedded
|
|
||||||
// defined types, then we may not have set their underlying
|
|
||||||
// interface type yet. So we need to defer calling Complete until
|
|
||||||
// after we've called SetUnderlying everywhere.
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): After CL 424876 lands, it should be safe to call
|
|
||||||
// iface.Complete() immediately.
|
|
||||||
r.p.ifaces = append(r.p.ifaces, iface)
|
|
||||||
|
|
||||||
return iface
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature {
|
|
||||||
r.Sync(pkgbits.SyncSignature)
|
|
||||||
|
|
||||||
params := r.params()
|
|
||||||
results := r.params()
|
|
||||||
variadic := r.Bool()
|
|
||||||
|
|
||||||
return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) params() *types.Tuple {
|
|
||||||
r.Sync(pkgbits.SyncParams)
|
|
||||||
|
|
||||||
params := make([]*types.Var, r.Len())
|
|
||||||
for i := range params {
|
|
||||||
params[i] = r.param()
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.NewTuple(params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) param() *types.Var {
|
|
||||||
r.Sync(pkgbits.SyncParam)
|
|
||||||
|
|
||||||
pos := r.pos()
|
|
||||||
pkg, name := r.localIdent()
|
|
||||||
typ := r.typ()
|
|
||||||
|
|
||||||
return types.NewParam(pos, pkg, name, typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @@@ Objects
|
|
||||||
|
|
||||||
func (r *reader) obj() (types.Object, []types.Type) {
|
|
||||||
r.Sync(pkgbits.SyncObject)
|
|
||||||
|
|
||||||
assert(!r.Bool())
|
|
||||||
|
|
||||||
pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj))
|
|
||||||
obj := pkgScope(pkg).Lookup(name)
|
|
||||||
|
|
||||||
targs := make([]types.Type, r.Len())
|
|
||||||
for i := range targs {
|
|
||||||
targs[i] = r.typ()
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj, targs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
|
|
||||||
|
|
||||||
var objPkg *types.Package
|
|
||||||
var objName string
|
|
||||||
var tag pkgbits.CodeObj
|
|
||||||
{
|
|
||||||
rname := pr.tempReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
|
|
||||||
|
|
||||||
objPkg, objName = rname.qualifiedIdent()
|
|
||||||
assert(objName != "")
|
|
||||||
|
|
||||||
tag = pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
|
|
||||||
pr.retireReader(rname)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tag == pkgbits.ObjStub {
|
|
||||||
assert(objPkg == nil || objPkg == types.Unsafe)
|
|
||||||
return objPkg, objName
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore local types promoted to global scope (#55110).
|
|
||||||
if _, suffix := splitVargenSuffix(objName); suffix != "" {
|
|
||||||
return objPkg, objName
|
|
||||||
}
|
|
||||||
|
|
||||||
if objPkg.Scope().Lookup(objName) == nil {
|
|
||||||
dict := pr.objDictIdx(idx)
|
|
||||||
|
|
||||||
r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
|
|
||||||
r.dict = dict
|
|
||||||
|
|
||||||
declare := func(obj types.Object) {
|
|
||||||
objPkg.Scope().Insert(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tag {
|
|
||||||
default:
|
|
||||||
panic("weird")
|
|
||||||
|
|
||||||
case pkgbits.ObjAlias:
|
|
||||||
pos := r.pos()
|
|
||||||
typ := r.typ()
|
|
||||||
declare(types.NewTypeName(pos, objPkg, objName, typ))
|
|
||||||
|
|
||||||
case pkgbits.ObjConst:
|
|
||||||
pos := r.pos()
|
|
||||||
typ := r.typ()
|
|
||||||
val := r.Value()
|
|
||||||
declare(types.NewConst(pos, objPkg, objName, typ, val))
|
|
||||||
|
|
||||||
case pkgbits.ObjFunc:
|
|
||||||
pos := r.pos()
|
|
||||||
tparams := r.typeParamNames()
|
|
||||||
sig := r.signature(nil, nil, tparams)
|
|
||||||
declare(types.NewFunc(pos, objPkg, objName, sig))
|
|
||||||
|
|
||||||
case pkgbits.ObjType:
|
|
||||||
pos := r.pos()
|
|
||||||
|
|
||||||
obj := types.NewTypeName(pos, objPkg, objName, nil)
|
|
||||||
named := types.NewNamed(obj, nil, nil)
|
|
||||||
declare(obj)
|
|
||||||
|
|
||||||
named.SetTypeParams(r.typeParamNames())
|
|
||||||
|
|
||||||
setUnderlying := func(underlying types.Type) {
|
|
||||||
// If the underlying type is an interface, we need to
|
|
||||||
// duplicate its methods so we can replace the receiver
|
|
||||||
// parameter's type (#49906).
|
|
||||||
if iface, ok := underlying.(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
|
|
||||||
methods := make([]*types.Func, iface.NumExplicitMethods())
|
|
||||||
for i := range methods {
|
|
||||||
fn := iface.ExplicitMethod(i)
|
|
||||||
sig := fn.Type().(*types.Signature)
|
|
||||||
|
|
||||||
recv := types.NewVar(fn.Pos(), fn.Pkg(), "", named)
|
|
||||||
methods[i] = types.NewFunc(fn.Pos(), fn.Pkg(), fn.Name(), types.NewSignature(recv, sig.Params(), sig.Results(), sig.Variadic()))
|
|
||||||
}
|
|
||||||
|
|
||||||
embeds := make([]types.Type, iface.NumEmbeddeds())
|
|
||||||
for i := range embeds {
|
|
||||||
embeds[i] = iface.EmbeddedType(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
newIface := types.NewInterfaceType(methods, embeds)
|
|
||||||
r.p.ifaces = append(r.p.ifaces, newIface)
|
|
||||||
underlying = newIface
|
|
||||||
}
|
|
||||||
|
|
||||||
named.SetUnderlying(underlying)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since go.dev/cl/455279, we can assume rhs.Underlying() will
|
|
||||||
// always be non-nil. However, to temporarily support users of
|
|
||||||
// older snapshot releases, we continue to fallback to the old
|
|
||||||
// behavior for now.
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): Remove fallback code and simplify after
|
|
||||||
// allowing time for snapshot users to upgrade.
|
|
||||||
rhs := r.typ()
|
|
||||||
if underlying := rhs.Underlying(); underlying != nil {
|
|
||||||
setUnderlying(underlying)
|
|
||||||
} else {
|
|
||||||
pk := r.p
|
|
||||||
pk.laterFor(named, func() {
|
|
||||||
// First be sure that the rhs is initialized, if it needs to be initialized.
|
|
||||||
delete(pk.laterFors, named) // prevent cycles
|
|
||||||
if i, ok := pk.laterFors[rhs]; ok {
|
|
||||||
f := pk.laterFns[i]
|
|
||||||
pk.laterFns[i] = func() {} // function is running now, so replace it with a no-op
|
|
||||||
f() // initialize RHS
|
|
||||||
}
|
|
||||||
setUnderlying(rhs.Underlying())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, n := 0, r.Len(); i < n; i++ {
|
|
||||||
named.AddMethod(r.method())
|
|
||||||
}
|
|
||||||
|
|
||||||
case pkgbits.ObjVar:
|
|
||||||
pos := r.pos()
|
|
||||||
typ := r.typ()
|
|
||||||
declare(types.NewVar(pos, objPkg, objName, typ))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return objPkg, objName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
|
|
||||||
|
|
||||||
var dict readerDict
|
|
||||||
|
|
||||||
{
|
|
||||||
r := pr.tempReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
|
|
||||||
if implicits := r.Len(); implicits != 0 {
|
|
||||||
errorf("unexpected object with %v implicit type parameter(s)", implicits)
|
|
||||||
}
|
|
||||||
|
|
||||||
dict.bounds = make([]typeInfo, r.Len())
|
|
||||||
for i := range dict.bounds {
|
|
||||||
dict.bounds[i] = r.typInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
dict.derived = make([]derivedInfo, r.Len())
|
|
||||||
dict.derivedTypes = make([]types.Type, len(dict.derived))
|
|
||||||
for i := range dict.derived {
|
|
||||||
dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()}
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.retireReader(r)
|
|
||||||
}
|
|
||||||
// function references follow, but reader doesn't need those
|
|
||||||
|
|
||||||
return &dict
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) typeParamNames() []*types.TypeParam {
|
|
||||||
r.Sync(pkgbits.SyncTypeParamNames)
|
|
||||||
|
|
||||||
// Note: This code assumes it only processes objects without
|
|
||||||
// implement type parameters. This is currently fine, because
|
|
||||||
// reader is only used to read in exported declarations, which are
|
|
||||||
// always package scoped.
|
|
||||||
|
|
||||||
if len(r.dict.bounds) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Careful: Type parameter lists may have cycles. To allow for this,
|
|
||||||
// we construct the type parameter list in two passes: first we
|
|
||||||
// create all the TypeNames and TypeParams, then we construct and
|
|
||||||
// set the bound type.
|
|
||||||
|
|
||||||
r.dict.tparams = make([]*types.TypeParam, len(r.dict.bounds))
|
|
||||||
for i := range r.dict.bounds {
|
|
||||||
pos := r.pos()
|
|
||||||
pkg, name := r.localIdent()
|
|
||||||
|
|
||||||
tname := types.NewTypeName(pos, pkg, name, nil)
|
|
||||||
r.dict.tparams[i] = types.NewTypeParam(tname, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
typs := make([]types.Type, len(r.dict.bounds))
|
|
||||||
for i, bound := range r.dict.bounds {
|
|
||||||
typs[i] = r.p.typIdx(bound, r.dict)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(mdempsky): This is subtle, elaborate further.
|
|
||||||
//
|
|
||||||
// We have to save tparams outside of the closure, because
|
|
||||||
// typeParamNames() can be called multiple times with the same
|
|
||||||
// dictionary instance.
|
|
||||||
//
|
|
||||||
// Also, this needs to happen later to make sure SetUnderlying has
|
|
||||||
// been called.
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): Is it safe to have a single "later" slice or do
|
|
||||||
// we need to have multiple passes? See comments on CL 386002 and
|
|
||||||
// go.dev/issue/52104.
|
|
||||||
tparams := r.dict.tparams
|
|
||||||
r.p.later(func() {
|
|
||||||
for i, typ := range typs {
|
|
||||||
tparams[i].SetConstraint(typ)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return r.dict.tparams
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) method() *types.Func {
|
|
||||||
r.Sync(pkgbits.SyncMethod)
|
|
||||||
pos := r.pos()
|
|
||||||
pkg, name := r.selector()
|
|
||||||
|
|
||||||
rparams := r.typeParamNames()
|
|
||||||
sig := r.signature(r.param(), rparams, nil)
|
|
||||||
|
|
||||||
_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
|
|
||||||
return types.NewFunc(pos, pkg, name, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) qualifiedIdent() (*types.Package, string) { return r.ident(pkgbits.SyncSym) }
|
|
||||||
func (r *reader) localIdent() (*types.Package, string) { return r.ident(pkgbits.SyncLocalIdent) }
|
|
||||||
func (r *reader) selector() (*types.Package, string) { return r.ident(pkgbits.SyncSelector) }
|
|
||||||
|
|
||||||
func (r *reader) ident(marker pkgbits.SyncMarker) (*types.Package, string) {
|
|
||||||
r.Sync(marker)
|
|
||||||
return r.pkg(), r.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// pkgScope returns pkg.Scope().
|
|
||||||
// If pkg is nil, it returns types.Universe instead.
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): Remove after x/tools can depend on Go 1.19.
|
|
||||||
func pkgScope(pkg *types.Package) *types.Scope {
|
|
||||||
if pkg != nil {
|
|
||||||
return pkg.Scope()
|
|
||||||
}
|
|
||||||
return types.Universe
|
|
||||||
}
|
|
|
@ -1,465 +0,0 @@
|
||||||
// Copyright 2020 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 gocommand is a helper for calling the go command.
|
|
||||||
package gocommand
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"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
|
|
||||||
// them if it sees a concurrency error.
|
|
||||||
type Runner struct {
|
|
||||||
// once guards the runner initialization.
|
|
||||||
once sync.Once
|
|
||||||
|
|
||||||
// inFlight tracks available workers.
|
|
||||||
inFlight chan struct{}
|
|
||||||
|
|
||||||
// serialized guards the ability to run a go command serially,
|
|
||||||
// to avoid deadlocks when claiming workers.
|
|
||||||
serialized chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxInFlight = 10
|
|
||||||
|
|
||||||
func (runner *Runner) initialize() {
|
|
||||||
runner.once.Do(func() {
|
|
||||||
runner.inFlight = make(chan struct{}, maxInFlight)
|
|
||||||
runner.serialized = make(chan struct{}, 1)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1.13: go: updates to go.mod needed, but contents have changed
|
|
||||||
// 1.14: go: updating go.mod: existing contents have changed since last read
|
|
||||||
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.
|
|
||||||
// It returns only stdout and a "friendly" 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)
|
|
||||||
return stdout, friendly
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunPiped runs the invocation serially, always waiting for any concurrent
|
|
||||||
// invocations to complete first.
|
|
||||||
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)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunRaw runs the invocation, serializing requests only if they fight over
|
|
||||||
// go.mod changes.
|
|
||||||
// Postcondition: both error results have same nilness.
|
|
||||||
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.
|
|
||||||
runner.initialize()
|
|
||||||
|
|
||||||
// First, try to run the go command concurrently.
|
|
||||||
stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv)
|
|
||||||
|
|
||||||
// If we encounter a load concurrency error, we need to retry serially.
|
|
||||||
if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) {
|
|
||||||
event.Error(ctx, "Load concurrency error, will retry serially", err)
|
|
||||||
|
|
||||||
// Run serially by calling runPiped.
|
|
||||||
stdout.Reset()
|
|
||||||
stderr.Reset()
|
|
||||||
friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stdout, stderr, friendlyErr, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Postcondition: both error results have same nilness.
|
|
||||||
func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
|
|
||||||
// Wait for 1 worker to become available.
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, nil, ctx.Err(), ctx.Err()
|
|
||||||
case runner.inFlight <- struct{}{}:
|
|
||||||
defer func() { <-runner.inFlight }()
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
|
|
||||||
friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr)
|
|
||||||
return stdout, stderr, friendlyErr, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Postcondition: both error results have same nilness.
|
|
||||||
func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) {
|
|
||||||
// Make sure the runner is always initialized.
|
|
||||||
runner.initialize()
|
|
||||||
|
|
||||||
// Acquire the serialization lock. This avoids deadlocks between two
|
|
||||||
// runPiped commands.
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err(), ctx.Err()
|
|
||||||
case runner.serialized <- struct{}{}:
|
|
||||||
defer func() { <-runner.serialized }()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all in-progress go commands to return before proceeding,
|
|
||||||
// to avoid load concurrency errors.
|
|
||||||
for i := 0; i < maxInFlight; i++ {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err(), ctx.Err()
|
|
||||||
case runner.inFlight <- struct{}{}:
|
|
||||||
// Make sure we always "return" any workers we took.
|
|
||||||
defer func() { <-runner.inFlight }()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inv.runWithFriendlyError(ctx, stdout, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// An Invocation represents a call to the go command.
|
|
||||||
type Invocation struct {
|
|
||||||
Verb string
|
|
||||||
Args []string
|
|
||||||
BuildFlags []string
|
|
||||||
|
|
||||||
// If ModFlag is set, the go command is invoked with -mod=ModFlag.
|
|
||||||
ModFlag string
|
|
||||||
|
|
||||||
// If ModFile is set, the go command is invoked with -modfile=ModFile.
|
|
||||||
ModFile string
|
|
||||||
|
|
||||||
// If Overlay is set, the go command is invoked with -overlay=Overlay.
|
|
||||||
Overlay string
|
|
||||||
|
|
||||||
// If CleanEnv is set, the invocation will run only with the environment
|
|
||||||
// in Env, not starting with os.Environ.
|
|
||||||
CleanEnv bool
|
|
||||||
Env []string
|
|
||||||
WorkingDir string
|
|
||||||
Logf func(format string, args ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Postcondition: both error results have same nilness.
|
|
||||||
func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
|
|
||||||
rawError = i.run(ctx, stdout, stderr)
|
|
||||||
if rawError != nil {
|
|
||||||
friendlyError = rawError
|
|
||||||
// Check for 'go' executable not being found.
|
|
||||||
if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
|
|
||||||
friendlyError = fmt.Errorf("go command required, not found: %v", ee)
|
|
||||||
}
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
friendlyError = ctx.Err()
|
|
||||||
}
|
|
||||||
friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
|
|
||||||
log := i.Logf
|
|
||||||
if log == nil {
|
|
||||||
log = func(string, ...interface{}) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
goArgs := []string{i.Verb}
|
|
||||||
|
|
||||||
appendModFile := func() {
|
|
||||||
if i.ModFile != "" {
|
|
||||||
goArgs = append(goArgs, "-modfile="+i.ModFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
appendModFlag := func() {
|
|
||||||
if i.ModFlag != "" {
|
|
||||||
goArgs = append(goArgs, "-mod="+i.ModFlag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
appendOverlayFlag := func() {
|
|
||||||
if i.Overlay != "" {
|
|
||||||
goArgs = append(goArgs, "-overlay="+i.Overlay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch i.Verb {
|
|
||||||
case "env", "version":
|
|
||||||
goArgs = append(goArgs, i.Args...)
|
|
||||||
case "mod":
|
|
||||||
// mod needs the sub-verb before flags.
|
|
||||||
goArgs = append(goArgs, i.Args[0])
|
|
||||||
appendModFile()
|
|
||||||
goArgs = append(goArgs, i.Args[1:]...)
|
|
||||||
case "get":
|
|
||||||
goArgs = append(goArgs, i.BuildFlags...)
|
|
||||||
appendModFile()
|
|
||||||
goArgs = append(goArgs, i.Args...)
|
|
||||||
|
|
||||||
default: // notably list and build.
|
|
||||||
goArgs = append(goArgs, i.BuildFlags...)
|
|
||||||
appendModFile()
|
|
||||||
appendModFlag()
|
|
||||||
appendOverlayFlag()
|
|
||||||
goArgs = append(goArgs, i.Args...)
|
|
||||||
}
|
|
||||||
cmd := exec.Command("go", goArgs...)
|
|
||||||
cmd.Stdout = stdout
|
|
||||||
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
|
|
||||||
// expects the working directory to keep the original path, including the
|
|
||||||
// go command when dealing with modules.
|
|
||||||
// The Go stdlib has a special feature where if the cwd and the PWD are the
|
|
||||||
// same node then it trusts the PWD, so by setting it in the env for the child
|
|
||||||
// process we fix up all the paths returned by the go command.
|
|
||||||
if !i.CleanEnv {
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
}
|
|
||||||
cmd.Env = append(cmd.Env, i.Env...)
|
|
||||||
if i.WorkingDir != "" {
|
|
||||||
cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
|
|
||||||
cmd.Dir = i.WorkingDir
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
|
|
||||||
|
|
||||||
return runCmdContext(ctx, cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugHangingGoCommands may be set by tests to enable additional
|
|
||||||
// instrumentation (including panics) for debugging hanging Go commands.
|
|
||||||
//
|
|
||||||
// See golang/go#54461 for details.
|
|
||||||
var DebugHangingGoCommands = false
|
|
||||||
|
|
||||||
// runCmdContext is like exec.CommandContext except it sends os.Interrupt
|
|
||||||
// before os.Kill.
|
|
||||||
func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
resChan := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
resChan <- cmd.Wait()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// If we're interested in debugging hanging Go commands, stop waiting after a
|
|
||||||
// minute and panic with interesting information.
|
|
||||||
debug := DebugHangingGoCommands
|
|
||||||
if debug {
|
|
||||||
timer := time.NewTimer(1 * time.Minute)
|
|
||||||
defer timer.Stop()
|
|
||||||
select {
|
|
||||||
case err := <-resChan:
|
|
||||||
return err
|
|
||||||
case <-timer.C:
|
|
||||||
HandleHangingGoCommand(cmd.Process)
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
select {
|
|
||||||
case err := <-resChan:
|
|
||||||
return err
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancelled. Interrupt and see if it ends voluntarily.
|
|
||||||
if err := cmd.Process.Signal(os.Interrupt); err == nil {
|
|
||||||
// (We used to wait only 1s but this proved
|
|
||||||
// fragile on loaded builder machines.)
|
|
||||||
timer := time.NewTimer(5 * 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.
|
|
||||||
// TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT
|
|
||||||
// on certain platforms, such as unix.
|
|
||||||
if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) && debug {
|
|
||||||
log.Printf("error killing the Go command: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return <-resChan
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleHangingGoCommand(proc *os.Process) {
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "linux", "darwin", "freebsd", "netbsd":
|
|
||||||
fmt.Fprintln(os.Stderr, `DETECTED A HANGING GO COMMAND
|
|
||||||
|
|
||||||
The gopls test runner has detected a hanging go command. In order to debug
|
|
||||||
this, the output of ps and lsof/fstat is printed below.
|
|
||||||
|
|
||||||
See golang/go#54461 for more details.`)
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stderr, "\nps axo ppid,pid,command:")
|
|
||||||
fmt.Fprintln(os.Stderr, "-------------------------")
|
|
||||||
psCmd := exec.Command("ps", "axo", "ppid,pid,command")
|
|
||||||
psCmd.Stdout = os.Stderr
|
|
||||||
psCmd.Stderr = os.Stderr
|
|
||||||
if err := psCmd.Run(); err != nil {
|
|
||||||
panic(fmt.Sprintf("running ps: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
listFiles := "lsof"
|
|
||||||
if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" {
|
|
||||||
listFiles = "fstat"
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stderr, "\n"+listFiles+":")
|
|
||||||
fmt.Fprintln(os.Stderr, "-----")
|
|
||||||
listFilesCmd := exec.Command(listFiles)
|
|
||||||
listFilesCmd.Stdout = os.Stderr
|
|
||||||
listFilesCmd.Stderr = os.Stderr
|
|
||||||
if err := listFilesCmd.Run(); err != nil {
|
|
||||||
panic(fmt.Sprintf("running %s: %v", listFiles, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("detected hanging go command (pid %d): see golang/go#54461 for more details", proc.Pid))
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdDebugStr(cmd *exec.Cmd) string {
|
|
||||||
env := make(map[string]string)
|
|
||||||
for _, kv := range cmd.Env {
|
|
||||||
split := strings.SplitN(kv, "=", 2)
|
|
||||||
if len(split) == 2 {
|
|
||||||
k, v := split[0], split[1]
|
|
||||||
env[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var args []string
|
|
||||||
for _, arg := range cmd.Args {
|
|
||||||
quoted := strconv.Quote(arg)
|
|
||||||
if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") {
|
|
||||||
args = append(args, quoted)
|
|
||||||
} else {
|
|
||||||
args = append(args, arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " "))
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
// Copyright 2020 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 gocommand
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/mod/semver"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ModuleJSON holds information about a module.
|
|
||||||
type ModuleJSON struct {
|
|
||||||
Path string // module path
|
|
||||||
Version string // module version
|
|
||||||
Versions []string // available module versions (with -versions)
|
|
||||||
Replace *ModuleJSON // replaced by this module
|
|
||||||
Time *time.Time // time version was created
|
|
||||||
Update *ModuleJSON // available update, if any (with -u)
|
|
||||||
Main bool // is this the main module?
|
|
||||||
Indirect bool // is this module only an indirect dependency of main module?
|
|
||||||
Dir string // directory holding files for this module, if any
|
|
||||||
GoMod string // path to go.mod file used when loading this module, if any
|
|
||||||
GoVersion string // go version used in module
|
|
||||||
}
|
|
||||||
|
|
||||||
var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
|
|
||||||
|
|
||||||
// VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands
|
|
||||||
// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
|
|
||||||
// of which only Verb and Args are modified to run the appropriate Go command.
|
|
||||||
// Inspired by setDefaultBuildMod in modload/init.go
|
|
||||||
func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, *ModuleJSON, error) {
|
|
||||||
mainMod, go114, err := getMainModuleAnd114(ctx, inv, r)
|
|
||||||
if err != nil {
|
|
||||||
return false, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We check the GOFLAGS to see if there is anything overridden or not.
|
|
||||||
inv.Verb = "env"
|
|
||||||
inv.Args = []string{"GOFLAGS"}
|
|
||||||
stdout, err := r.Run(ctx, inv)
|
|
||||||
if err != nil {
|
|
||||||
return false, nil, err
|
|
||||||
}
|
|
||||||
goflags := string(bytes.TrimSpace(stdout.Bytes()))
|
|
||||||
matches := modFlagRegexp.FindStringSubmatch(goflags)
|
|
||||||
var modFlag string
|
|
||||||
if len(matches) != 0 {
|
|
||||||
modFlag = matches[1]
|
|
||||||
}
|
|
||||||
// Don't override an explicit '-mod=' argument.
|
|
||||||
if modFlag == "vendor" {
|
|
||||||
return true, mainMod, nil
|
|
||||||
} else if modFlag != "" {
|
|
||||||
return false, nil, nil
|
|
||||||
}
|
|
||||||
if mainMod == nil || !go114 {
|
|
||||||
return false, nil, nil
|
|
||||||
}
|
|
||||||
// Check 1.14's automatic vendor mode.
|
|
||||||
if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
|
|
||||||
if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
|
|
||||||
// The Go version is at least 1.14, and a vendor directory exists.
|
|
||||||
// Set -mod=vendor by default.
|
|
||||||
return true, mainMod, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMainModuleAnd114 gets one of the main modules' information and whether the
|
|
||||||
// go command in use is 1.14+. This is the information needed to figure out
|
|
||||||
// if vendoring should be enabled.
|
|
||||||
func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
|
|
||||||
const format = `{{.Path}}
|
|
||||||
{{.Dir}}
|
|
||||||
{{.GoMod}}
|
|
||||||
{{.GoVersion}}
|
|
||||||
{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
|
|
||||||
`
|
|
||||||
inv.Verb = "list"
|
|
||||||
inv.Args = []string{"-m", "-f", format}
|
|
||||||
stdout, err := r.Run(ctx, inv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(stdout.String(), "\n")
|
|
||||||
if len(lines) < 5 {
|
|
||||||
return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String())
|
|
||||||
}
|
|
||||||
mod := &ModuleJSON{
|
|
||||||
Path: lines[0],
|
|
||||||
Dir: lines[1],
|
|
||||||
GoMod: lines[2],
|
|
||||||
GoVersion: lines[3],
|
|
||||||
Main: true,
|
|
||||||
}
|
|
||||||
return mod, lines[4] == "go1.14", nil
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
// Copyright 2020 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 gocommand
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GoVersion reports the minor version number of the highest release
|
|
||||||
// tag built into the go command on the PATH.
|
|
||||||
//
|
|
||||||
// Note that this may be higher than the version of the go tool used
|
|
||||||
// to build this application, and thus the versions of the standard
|
|
||||||
// go/{scanner,parser,ast,types} packages that are linked into it.
|
|
||||||
// In that case, callers should either downgrade to the version of
|
|
||||||
// go used to build the application, or report an error that the
|
|
||||||
// application is too old to use the go command on the PATH.
|
|
||||||
func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
|
|
||||||
inv.Verb = "list"
|
|
||||||
inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
|
|
||||||
inv.BuildFlags = nil // This is not a build command.
|
|
||||||
inv.ModFlag = ""
|
|
||||||
inv.ModFile = ""
|
|
||||||
inv.Env = append(inv.Env[:len(inv.Env):len(inv.Env)], "GO111MODULE=off")
|
|
||||||
|
|
||||||
stdoutBytes, err := r.Run(ctx, inv)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
stdout := stdoutBytes.String()
|
|
||||||
if len(stdout) < 3 {
|
|
||||||
return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout)
|
|
||||||
}
|
|
||||||
// Split up "[go1.1 go1.15]" and return highest go1.X value.
|
|
||||||
tags := strings.Fields(stdout[1 : len(stdout)-2])
|
|
||||||
for i := len(tags) - 1; i >= 0; i-- {
|
|
||||||
var version int
|
|
||||||
if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return version, nil
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GoVersionOutput returns the complete output of the go version command.
|
|
||||||
func GoVersionOutput(ctx context.Context, inv Invocation, r *Runner) (string, error) {
|
|
||||||
inv.Verb = "version"
|
|
||||||
goVersion, err := r.Run(ctx, inv)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return goVersion.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseGoVersionOutput extracts the Go version string
|
|
||||||
// from the output of the "go version" command.
|
|
||||||
// Given an unrecognized form, it returns an empty string.
|
|
||||||
func ParseGoVersionOutput(data string) string {
|
|
||||||
re := regexp.MustCompile(`^go version (go\S+|devel \S+)`)
|
|
||||||
m := re.FindStringSubmatch(data)
|
|
||||||
if len(m) != 2 {
|
|
||||||
return "" // unrecognized version
|
|
||||||
}
|
|
||||||
return m[1]
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2020 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 packagesinternal exposes internal-only fields from go/packages.
|
|
||||||
package packagesinternal
|
|
||||||
|
|
||||||
var GetForTest = func(p interface{}) string { return "" }
|
|
||||||
var GetDepsErrors = func(p interface{}) []*PackageError { return nil }
|
|
||||||
|
|
||||||
type PackageError struct {
|
|
||||||
ImportStack []string // shortest path from package named on command line to this one
|
|
||||||
Pos string // position of error (if present, file:line:col)
|
|
||||||
Err string // the error itself
|
|
||||||
}
|
|
||||||
|
|
||||||
var TypecheckCgo int
|
|
||||||
var DepsErrors int // must be set as a LoadMode to call GetDepsErrors
|
|
||||||
var ForTest int // must be set as a LoadMode to call GetForTest
|
|
||||||
|
|
||||||
var SetModFlag = func(config interface{}, value string) {}
|
|
||||||
var SetModFile = func(config interface{}, value string) {}
|
|
|
@ -1,77 +0,0 @@
|
||||||
// Copyright 2021 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 pkgbits
|
|
||||||
|
|
||||||
// A Code is an enum value that can be encoded into bitstreams.
|
|
||||||
//
|
|
||||||
// Code types are preferable for enum types, because they allow
|
|
||||||
// Decoder to detect desyncs.
|
|
||||||
type Code interface {
|
|
||||||
// Marker returns the SyncMarker for the Code's dynamic type.
|
|
||||||
Marker() SyncMarker
|
|
||||||
|
|
||||||
// Value returns the Code's ordinal value.
|
|
||||||
Value() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// A CodeVal distinguishes among go/constant.Value encodings.
|
|
||||||
type CodeVal int
|
|
||||||
|
|
||||||
func (c CodeVal) Marker() SyncMarker { return SyncVal }
|
|
||||||
func (c CodeVal) Value() int { return int(c) }
|
|
||||||
|
|
||||||
// Note: These values are public and cannot be changed without
|
|
||||||
// updating the go/types importers.
|
|
||||||
|
|
||||||
const (
|
|
||||||
ValBool CodeVal = iota
|
|
||||||
ValString
|
|
||||||
ValInt64
|
|
||||||
ValBigInt
|
|
||||||
ValBigRat
|
|
||||||
ValBigFloat
|
|
||||||
)
|
|
||||||
|
|
||||||
// A CodeType distinguishes among go/types.Type encodings.
|
|
||||||
type CodeType int
|
|
||||||
|
|
||||||
func (c CodeType) Marker() SyncMarker { return SyncType }
|
|
||||||
func (c CodeType) Value() int { return int(c) }
|
|
||||||
|
|
||||||
// Note: These values are public and cannot be changed without
|
|
||||||
// updating the go/types importers.
|
|
||||||
|
|
||||||
const (
|
|
||||||
TypeBasic CodeType = iota
|
|
||||||
TypeNamed
|
|
||||||
TypePointer
|
|
||||||
TypeSlice
|
|
||||||
TypeArray
|
|
||||||
TypeChan
|
|
||||||
TypeMap
|
|
||||||
TypeSignature
|
|
||||||
TypeStruct
|
|
||||||
TypeInterface
|
|
||||||
TypeUnion
|
|
||||||
TypeTypeParam
|
|
||||||
)
|
|
||||||
|
|
||||||
// A CodeObj distinguishes among go/types.Object encodings.
|
|
||||||
type CodeObj int
|
|
||||||
|
|
||||||
func (c CodeObj) Marker() SyncMarker { return SyncCodeObj }
|
|
||||||
func (c CodeObj) Value() int { return int(c) }
|
|
||||||
|
|
||||||
// Note: These values are public and cannot be changed without
|
|
||||||
// updating the go/types importers.
|
|
||||||
|
|
||||||
const (
|
|
||||||
ObjAlias CodeObj = iota
|
|
||||||
ObjConst
|
|
||||||
ObjType
|
|
||||||
ObjFunc
|
|
||||||
ObjVar
|
|
||||||
ObjStub
|
|
||||||
)
|
|
|
@ -1,517 +0,0 @@
|
||||||
// Copyright 2021 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 pkgbits
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"go/constant"
|
|
||||||
"go/token"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A PkgDecoder provides methods for decoding a package's Unified IR
|
|
||||||
// export data.
|
|
||||||
type PkgDecoder struct {
|
|
||||||
// version is the file format version.
|
|
||||||
version uint32
|
|
||||||
|
|
||||||
// sync indicates whether the file uses sync markers.
|
|
||||||
sync bool
|
|
||||||
|
|
||||||
// pkgPath is the package path for the package to be decoded.
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): Remove; unneeded since CL 391014.
|
|
||||||
pkgPath string
|
|
||||||
|
|
||||||
// elemData is the full data payload of the encoded package.
|
|
||||||
// Elements are densely and contiguously packed together.
|
|
||||||
//
|
|
||||||
// The last 8 bytes of elemData are the package fingerprint.
|
|
||||||
elemData string
|
|
||||||
|
|
||||||
// elemEnds stores the byte-offset end positions of element
|
|
||||||
// bitstreams within elemData.
|
|
||||||
//
|
|
||||||
// For example, element I's bitstream data starts at elemEnds[I-1]
|
|
||||||
// (or 0, if I==0) and ends at elemEnds[I].
|
|
||||||
//
|
|
||||||
// Note: elemEnds is indexed by absolute indices, not
|
|
||||||
// section-relative indices.
|
|
||||||
elemEnds []uint32
|
|
||||||
|
|
||||||
// elemEndsEnds stores the index-offset end positions of relocation
|
|
||||||
// sections within elemEnds.
|
|
||||||
//
|
|
||||||
// For example, section K's end positions start at elemEndsEnds[K-1]
|
|
||||||
// (or 0, if K==0) and end at elemEndsEnds[K].
|
|
||||||
elemEndsEnds [numRelocs]uint32
|
|
||||||
|
|
||||||
scratchRelocEnt []RelocEnt
|
|
||||||
}
|
|
||||||
|
|
||||||
// PkgPath returns the package path for the package
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): Remove; unneeded since CL 391014.
|
|
||||||
func (pr *PkgDecoder) PkgPath() string { return pr.pkgPath }
|
|
||||||
|
|
||||||
// SyncMarkers reports whether pr uses sync markers.
|
|
||||||
func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync }
|
|
||||||
|
|
||||||
// NewPkgDecoder returns a PkgDecoder initialized to read the Unified
|
|
||||||
// IR export data from input. pkgPath is the package path for the
|
|
||||||
// compilation unit that produced the export data.
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014.
|
|
||||||
func NewPkgDecoder(pkgPath, input string) PkgDecoder {
|
|
||||||
pr := PkgDecoder{
|
|
||||||
pkgPath: pkgPath,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(mdempsky): Implement direct indexing of input string to
|
|
||||||
// avoid copying the position information.
|
|
||||||
|
|
||||||
r := strings.NewReader(input)
|
|
||||||
|
|
||||||
assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil)
|
|
||||||
|
|
||||||
switch pr.version {
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unsupported version: %v", pr.version))
|
|
||||||
case 0:
|
|
||||||
// no flags
|
|
||||||
case 1:
|
|
||||||
var flags uint32
|
|
||||||
assert(binary.Read(r, binary.LittleEndian, &flags) == nil)
|
|
||||||
pr.sync = flags&flagSyncMarkers != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil)
|
|
||||||
|
|
||||||
pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1])
|
|
||||||
assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil)
|
|
||||||
|
|
||||||
pos, err := r.Seek(0, io.SeekCurrent)
|
|
||||||
assert(err == nil)
|
|
||||||
|
|
||||||
pr.elemData = input[pos:]
|
|
||||||
assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1]))
|
|
||||||
|
|
||||||
return pr
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumElems returns the number of elements in section k.
|
|
||||||
func (pr *PkgDecoder) NumElems(k RelocKind) int {
|
|
||||||
count := int(pr.elemEndsEnds[k])
|
|
||||||
if k > 0 {
|
|
||||||
count -= int(pr.elemEndsEnds[k-1])
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
// TotalElems returns the total number of elements across all sections.
|
|
||||||
func (pr *PkgDecoder) TotalElems() int {
|
|
||||||
return len(pr.elemEnds)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fingerprint returns the package fingerprint.
|
|
||||||
func (pr *PkgDecoder) Fingerprint() [8]byte {
|
|
||||||
var fp [8]byte
|
|
||||||
copy(fp[:], pr.elemData[len(pr.elemData)-8:])
|
|
||||||
return fp
|
|
||||||
}
|
|
||||||
|
|
||||||
// AbsIdx returns the absolute index for the given (section, index)
|
|
||||||
// pair.
|
|
||||||
func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int {
|
|
||||||
absIdx := int(idx)
|
|
||||||
if k > 0 {
|
|
||||||
absIdx += int(pr.elemEndsEnds[k-1])
|
|
||||||
}
|
|
||||||
if absIdx >= int(pr.elemEndsEnds[k]) {
|
|
||||||
errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds)
|
|
||||||
}
|
|
||||||
return absIdx
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataIdx returns the raw element bitstream for the given (section,
|
|
||||||
// index) pair.
|
|
||||||
func (pr *PkgDecoder) DataIdx(k RelocKind, idx Index) string {
|
|
||||||
absIdx := pr.AbsIdx(k, idx)
|
|
||||||
|
|
||||||
var start uint32
|
|
||||||
if absIdx > 0 {
|
|
||||||
start = pr.elemEnds[absIdx-1]
|
|
||||||
}
|
|
||||||
end := pr.elemEnds[absIdx]
|
|
||||||
|
|
||||||
return pr.elemData[start:end]
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringIdx returns the string value for the given string index.
|
|
||||||
func (pr *PkgDecoder) StringIdx(idx Index) string {
|
|
||||||
return pr.DataIdx(RelocString, idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDecoder returns a Decoder for the given (section, index) pair,
|
|
||||||
// and decodes the given SyncMarker from the element bitstream.
|
|
||||||
func (pr *PkgDecoder) NewDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder {
|
|
||||||
r := pr.NewDecoderRaw(k, idx)
|
|
||||||
r.Sync(marker)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// TempDecoder returns a Decoder for the given (section, index) pair,
|
|
||||||
// and decodes the given SyncMarker from the element bitstream.
|
|
||||||
// If possible the Decoder should be RetireDecoder'd when it is no longer
|
|
||||||
// needed, this will avoid heap allocations.
|
|
||||||
func (pr *PkgDecoder) TempDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder {
|
|
||||||
r := pr.TempDecoderRaw(k, idx)
|
|
||||||
r.Sync(marker)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *PkgDecoder) RetireDecoder(d *Decoder) {
|
|
||||||
pr.scratchRelocEnt = d.Relocs
|
|
||||||
d.Relocs = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDecoderRaw returns a Decoder for the given (section, index) pair.
|
|
||||||
//
|
|
||||||
// Most callers should use NewDecoder instead.
|
|
||||||
func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder {
|
|
||||||
r := Decoder{
|
|
||||||
common: pr,
|
|
||||||
k: k,
|
|
||||||
Idx: idx,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(mdempsky) r.data.Reset(...) after #44505 is resolved.
|
|
||||||
r.Data = *strings.NewReader(pr.DataIdx(k, idx))
|
|
||||||
|
|
||||||
r.Sync(SyncRelocs)
|
|
||||||
r.Relocs = make([]RelocEnt, r.Len())
|
|
||||||
for i := range r.Relocs {
|
|
||||||
r.Sync(SyncReloc)
|
|
||||||
r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pr *PkgDecoder) TempDecoderRaw(k RelocKind, idx Index) Decoder {
|
|
||||||
r := Decoder{
|
|
||||||
common: pr,
|
|
||||||
k: k,
|
|
||||||
Idx: idx,
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Data.Reset(pr.DataIdx(k, idx))
|
|
||||||
r.Sync(SyncRelocs)
|
|
||||||
l := r.Len()
|
|
||||||
if cap(pr.scratchRelocEnt) >= l {
|
|
||||||
r.Relocs = pr.scratchRelocEnt[:l]
|
|
||||||
pr.scratchRelocEnt = nil
|
|
||||||
} else {
|
|
||||||
r.Relocs = make([]RelocEnt, l)
|
|
||||||
}
|
|
||||||
for i := range r.Relocs {
|
|
||||||
r.Sync(SyncReloc)
|
|
||||||
r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Decoder provides methods for decoding an individual element's
|
|
||||||
// bitstream data.
|
|
||||||
type Decoder struct {
|
|
||||||
common *PkgDecoder
|
|
||||||
|
|
||||||
Relocs []RelocEnt
|
|
||||||
Data strings.Reader
|
|
||||||
|
|
||||||
k RelocKind
|
|
||||||
Idx Index
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Decoder) checkErr(err error) {
|
|
||||||
if err != nil {
|
|
||||||
errorf("unexpected decoding error: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Decoder) rawUvarint() uint64 {
|
|
||||||
x, err := readUvarint(&r.Data)
|
|
||||||
r.checkErr(err)
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUvarint is a type-specialized copy of encoding/binary.ReadUvarint.
|
|
||||||
// This avoids the interface conversion and thus has better escape properties,
|
|
||||||
// which flows up the stack.
|
|
||||||
func readUvarint(r *strings.Reader) (uint64, error) {
|
|
||||||
var x uint64
|
|
||||||
var s uint
|
|
||||||
for i := 0; i < binary.MaxVarintLen64; i++ {
|
|
||||||
b, err := r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
if i > 0 && err == io.EOF {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return x, err
|
|
||||||
}
|
|
||||||
if b < 0x80 {
|
|
||||||
if i == binary.MaxVarintLen64-1 && b > 1 {
|
|
||||||
return x, overflow
|
|
||||||
}
|
|
||||||
return x | uint64(b)<<s, nil
|
|
||||||
}
|
|
||||||
x |= uint64(b&0x7f) << s
|
|
||||||
s += 7
|
|
||||||
}
|
|
||||||
return x, overflow
|
|
||||||
}
|
|
||||||
|
|
||||||
var overflow = errors.New("pkgbits: readUvarint overflows a 64-bit integer")
|
|
||||||
|
|
||||||
func (r *Decoder) rawVarint() int64 {
|
|
||||||
ux := r.rawUvarint()
|
|
||||||
|
|
||||||
// Zig-zag decode.
|
|
||||||
x := int64(ux >> 1)
|
|
||||||
if ux&1 != 0 {
|
|
||||||
x = ^x
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Decoder) rawReloc(k RelocKind, idx int) Index {
|
|
||||||
e := r.Relocs[idx]
|
|
||||||
assert(e.Kind == k)
|
|
||||||
return e.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync decodes a sync marker from the element bitstream and asserts
|
|
||||||
// that it matches the expected marker.
|
|
||||||
//
|
|
||||||
// If r.common.sync is false, then Sync is a no-op.
|
|
||||||
func (r *Decoder) Sync(mWant SyncMarker) {
|
|
||||||
if !r.common.sync {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pos, _ := r.Data.Seek(0, io.SeekCurrent)
|
|
||||||
mHave := SyncMarker(r.rawUvarint())
|
|
||||||
writerPCs := make([]int, r.rawUvarint())
|
|
||||||
for i := range writerPCs {
|
|
||||||
writerPCs[i] = int(r.rawUvarint())
|
|
||||||
}
|
|
||||||
|
|
||||||
if mHave == mWant {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's some tension here between printing:
|
|
||||||
//
|
|
||||||
// (1) full file paths that tools can recognize (e.g., so emacs
|
|
||||||
// hyperlinks the "file:line" text for easy navigation), or
|
|
||||||
//
|
|
||||||
// (2) short file paths that are easier for humans to read (e.g., by
|
|
||||||
// omitting redundant or irrelevant details, so it's easier to
|
|
||||||
// focus on the useful bits that remain).
|
|
||||||
//
|
|
||||||
// The current formatting favors the former, as it seems more
|
|
||||||
// helpful in practice. But perhaps the formatting could be improved
|
|
||||||
// to better address both concerns. For example, use relative file
|
|
||||||
// paths if they would be shorter, or rewrite file paths to contain
|
|
||||||
// "$GOROOT" (like objabi.AbsFile does) if tools can be taught how
|
|
||||||
// to reliably expand that again.
|
|
||||||
|
|
||||||
fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.Idx, pos)
|
|
||||||
|
|
||||||
fmt.Printf("\nfound %v, written at:\n", mHave)
|
|
||||||
if len(writerPCs) == 0 {
|
|
||||||
fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath)
|
|
||||||
}
|
|
||||||
for _, pc := range writerPCs {
|
|
||||||
fmt.Printf("\t%s\n", r.common.StringIdx(r.rawReloc(RelocString, pc)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("\nexpected %v, reading at:\n", mWant)
|
|
||||||
var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size?
|
|
||||||
n := runtime.Callers(2, readerPCs[:])
|
|
||||||
for _, pc := range fmtFrames(readerPCs[:n]...) {
|
|
||||||
fmt.Printf("\t%s\n", pc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We already printed a stack trace for the reader, so now we can
|
|
||||||
// simply exit. Printing a second one with panic or base.Fatalf
|
|
||||||
// would just be noise.
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool decodes and returns a bool value from the element bitstream.
|
|
||||||
func (r *Decoder) Bool() bool {
|
|
||||||
r.Sync(SyncBool)
|
|
||||||
x, err := r.Data.ReadByte()
|
|
||||||
r.checkErr(err)
|
|
||||||
assert(x < 2)
|
|
||||||
return x != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 decodes and returns an int64 value from the element bitstream.
|
|
||||||
func (r *Decoder) Int64() int64 {
|
|
||||||
r.Sync(SyncInt64)
|
|
||||||
return r.rawVarint()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64 decodes and returns a uint64 value from the element bitstream.
|
|
||||||
func (r *Decoder) Uint64() uint64 {
|
|
||||||
r.Sync(SyncUint64)
|
|
||||||
return r.rawUvarint()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len decodes and returns a non-negative int value from the element bitstream.
|
|
||||||
func (r *Decoder) Len() int { x := r.Uint64(); v := int(x); assert(uint64(v) == x); return v }
|
|
||||||
|
|
||||||
// Int decodes and returns an int value from the element bitstream.
|
|
||||||
func (r *Decoder) Int() int { x := r.Int64(); v := int(x); assert(int64(v) == x); return v }
|
|
||||||
|
|
||||||
// Uint decodes and returns a uint value from the element bitstream.
|
|
||||||
func (r *Decoder) Uint() uint { x := r.Uint64(); v := uint(x); assert(uint64(v) == x); return v }
|
|
||||||
|
|
||||||
// Code decodes a Code value from the element bitstream and returns
|
|
||||||
// its ordinal value. It's the caller's responsibility to convert the
|
|
||||||
// result to an appropriate Code type.
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): Ideally this method would have signature "Code[T
|
|
||||||
// Code] T" instead, but we don't allow generic methods and the
|
|
||||||
// compiler can't depend on generics yet anyway.
|
|
||||||
func (r *Decoder) Code(mark SyncMarker) int {
|
|
||||||
r.Sync(mark)
|
|
||||||
return r.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reloc decodes a relocation of expected section k from the element
|
|
||||||
// bitstream and returns an index to the referenced element.
|
|
||||||
func (r *Decoder) Reloc(k RelocKind) Index {
|
|
||||||
r.Sync(SyncUseReloc)
|
|
||||||
return r.rawReloc(k, r.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
// String decodes and returns a string value from the element
|
|
||||||
// bitstream.
|
|
||||||
func (r *Decoder) String() string {
|
|
||||||
r.Sync(SyncString)
|
|
||||||
return r.common.StringIdx(r.Reloc(RelocString))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings decodes and returns a variable-length slice of strings from
|
|
||||||
// the element bitstream.
|
|
||||||
func (r *Decoder) Strings() []string {
|
|
||||||
res := make([]string, r.Len())
|
|
||||||
for i := range res {
|
|
||||||
res[i] = r.String()
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value decodes and returns a constant.Value from the element
|
|
||||||
// bitstream.
|
|
||||||
func (r *Decoder) Value() constant.Value {
|
|
||||||
r.Sync(SyncValue)
|
|
||||||
isComplex := r.Bool()
|
|
||||||
val := r.scalar()
|
|
||||||
if isComplex {
|
|
||||||
val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar()))
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Decoder) scalar() constant.Value {
|
|
||||||
switch tag := CodeVal(r.Code(SyncVal)); tag {
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unexpected scalar tag: %v", tag))
|
|
||||||
|
|
||||||
case ValBool:
|
|
||||||
return constant.MakeBool(r.Bool())
|
|
||||||
case ValString:
|
|
||||||
return constant.MakeString(r.String())
|
|
||||||
case ValInt64:
|
|
||||||
return constant.MakeInt64(r.Int64())
|
|
||||||
case ValBigInt:
|
|
||||||
return constant.Make(r.bigInt())
|
|
||||||
case ValBigRat:
|
|
||||||
num := r.bigInt()
|
|
||||||
denom := r.bigInt()
|
|
||||||
return constant.Make(new(big.Rat).SetFrac(num, denom))
|
|
||||||
case ValBigFloat:
|
|
||||||
return constant.Make(r.bigFloat())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Decoder) bigInt() *big.Int {
|
|
||||||
v := new(big.Int).SetBytes([]byte(r.String()))
|
|
||||||
if r.Bool() {
|
|
||||||
v.Neg(v)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Decoder) bigFloat() *big.Float {
|
|
||||||
v := new(big.Float).SetPrec(512)
|
|
||||||
assert(v.UnmarshalText([]byte(r.String())) == nil)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// @@@ Helpers
|
|
||||||
|
|
||||||
// TODO(mdempsky): These should probably be removed. I think they're a
|
|
||||||
// smell that the export data format is not yet quite right.
|
|
||||||
|
|
||||||
// PeekPkgPath returns the package path for the specified package
|
|
||||||
// index.
|
|
||||||
func (pr *PkgDecoder) PeekPkgPath(idx Index) string {
|
|
||||||
var path string
|
|
||||||
{
|
|
||||||
r := pr.TempDecoder(RelocPkg, idx, SyncPkgDef)
|
|
||||||
path = r.String()
|
|
||||||
pr.RetireDecoder(&r)
|
|
||||||
}
|
|
||||||
if path == "" {
|
|
||||||
path = pr.pkgPath
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeekObj returns the package path, object name, and CodeObj for the
|
|
||||||
// specified object index.
|
|
||||||
func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) {
|
|
||||||
var ridx Index
|
|
||||||
var name string
|
|
||||||
var rcode int
|
|
||||||
{
|
|
||||||
r := pr.TempDecoder(RelocName, idx, SyncObject1)
|
|
||||||
r.Sync(SyncSym)
|
|
||||||
r.Sync(SyncPkg)
|
|
||||||
ridx = r.Reloc(RelocPkg)
|
|
||||||
name = r.String()
|
|
||||||
rcode = r.Code(SyncCodeObj)
|
|
||||||
pr.RetireDecoder(&r)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := pr.PeekPkgPath(ridx)
|
|
||||||
assert(name != "")
|
|
||||||
|
|
||||||
tag := CodeObj(rcode)
|
|
||||||
|
|
||||||
return path, name, tag
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
// Copyright 2022 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 pkgbits implements low-level coding abstractions for
|
|
||||||
// Unified IR's export data format.
|
|
||||||
//
|
|
||||||
// At a low-level, a package is a collection of bitstream elements.
|
|
||||||
// Each element has a "kind" and a dense, non-negative index.
|
|
||||||
// Elements can be randomly accessed given their kind and index.
|
|
||||||
//
|
|
||||||
// Individual elements are sequences of variable-length values (e.g.,
|
|
||||||
// integers, booleans, strings, go/constant values, cross-references
|
|
||||||
// to other elements). Package pkgbits provides APIs for encoding and
|
|
||||||
// decoding these low-level values, but the details of mapping
|
|
||||||
// higher-level Go constructs into elements is left to higher-level
|
|
||||||
// abstractions.
|
|
||||||
//
|
|
||||||
// Elements may cross-reference each other with "relocations." For
|
|
||||||
// example, an element representing a pointer type has a relocation
|
|
||||||
// referring to the element type.
|
|
||||||
//
|
|
||||||
// Go constructs may be composed as a constellation of multiple
|
|
||||||
// elements. For example, a declared function may have one element to
|
|
||||||
// describe the object (e.g., its name, type, position), and a
|
|
||||||
// separate element to describe its function body. This allows readers
|
|
||||||
// some flexibility in efficiently seeking or re-reading data (e.g.,
|
|
||||||
// inlining requires re-reading the function body for each inlined
|
|
||||||
// call, without needing to re-read the object-level details).
|
|
||||||
//
|
|
||||||
// This is a copy of internal/pkgbits in the Go implementation.
|
|
||||||
package pkgbits
|
|
|
@ -1,383 +0,0 @@
|
||||||
// Copyright 2021 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 pkgbits
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/binary"
|
|
||||||
"go/constant"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// currentVersion is the current version number.
|
|
||||||
//
|
|
||||||
// - v0: initial prototype
|
|
||||||
//
|
|
||||||
// - v1: adds the flags uint32 word
|
|
||||||
const currentVersion uint32 = 1
|
|
||||||
|
|
||||||
// A PkgEncoder provides methods for encoding a package's Unified IR
|
|
||||||
// export data.
|
|
||||||
type PkgEncoder struct {
|
|
||||||
// elems holds the bitstream for previously encoded elements.
|
|
||||||
elems [numRelocs][]string
|
|
||||||
|
|
||||||
// stringsIdx maps previously encoded strings to their index within
|
|
||||||
// the RelocString section, to allow deduplication. That is,
|
|
||||||
// elems[RelocString][stringsIdx[s]] == s (if present).
|
|
||||||
stringsIdx map[string]Index
|
|
||||||
|
|
||||||
// syncFrames is the number of frames to write at each sync
|
|
||||||
// marker. A negative value means sync markers are omitted.
|
|
||||||
syncFrames int
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncMarkers reports whether pw uses sync markers.
|
|
||||||
func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 }
|
|
||||||
|
|
||||||
// NewPkgEncoder returns an initialized PkgEncoder.
|
|
||||||
//
|
|
||||||
// syncFrames is the number of caller frames that should be serialized
|
|
||||||
// at Sync points. Serializing additional frames results in larger
|
|
||||||
// export data files, but can help diagnosing desync errors in
|
|
||||||
// higher-level Unified IR reader/writer code. If syncFrames is
|
|
||||||
// negative, then sync markers are omitted entirely.
|
|
||||||
func NewPkgEncoder(syncFrames int) PkgEncoder {
|
|
||||||
return PkgEncoder{
|
|
||||||
stringsIdx: make(map[string]Index),
|
|
||||||
syncFrames: syncFrames,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DumpTo writes the package's encoded data to out0 and returns the
|
|
||||||
// package fingerprint.
|
|
||||||
func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) {
|
|
||||||
h := md5.New()
|
|
||||||
out := io.MultiWriter(out0, h)
|
|
||||||
|
|
||||||
writeUint32 := func(x uint32) {
|
|
||||||
assert(binary.Write(out, binary.LittleEndian, x) == nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
writeUint32(currentVersion)
|
|
||||||
|
|
||||||
var flags uint32
|
|
||||||
if pw.SyncMarkers() {
|
|
||||||
flags |= flagSyncMarkers
|
|
||||||
}
|
|
||||||
writeUint32(flags)
|
|
||||||
|
|
||||||
// Write elemEndsEnds.
|
|
||||||
var sum uint32
|
|
||||||
for _, elems := range &pw.elems {
|
|
||||||
sum += uint32(len(elems))
|
|
||||||
writeUint32(sum)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write elemEnds.
|
|
||||||
sum = 0
|
|
||||||
for _, elems := range &pw.elems {
|
|
||||||
for _, elem := range elems {
|
|
||||||
sum += uint32(len(elem))
|
|
||||||
writeUint32(sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write elemData.
|
|
||||||
for _, elems := range &pw.elems {
|
|
||||||
for _, elem := range elems {
|
|
||||||
_, err := io.WriteString(out, elem)
|
|
||||||
assert(err == nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write fingerprint.
|
|
||||||
copy(fingerprint[:], h.Sum(nil))
|
|
||||||
_, err := out0.Write(fingerprint[:])
|
|
||||||
assert(err == nil)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringIdx adds a string value to the strings section, if not
|
|
||||||
// already present, and returns its index.
|
|
||||||
func (pw *PkgEncoder) StringIdx(s string) Index {
|
|
||||||
if idx, ok := pw.stringsIdx[s]; ok {
|
|
||||||
assert(pw.elems[RelocString][idx] == s)
|
|
||||||
return idx
|
|
||||||
}
|
|
||||||
|
|
||||||
idx := Index(len(pw.elems[RelocString]))
|
|
||||||
pw.elems[RelocString] = append(pw.elems[RelocString], s)
|
|
||||||
pw.stringsIdx[s] = idx
|
|
||||||
return idx
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoder returns an Encoder for a new element within the given
|
|
||||||
// section, and encodes the given SyncMarker as the start of the
|
|
||||||
// element bitstream.
|
|
||||||
func (pw *PkgEncoder) NewEncoder(k RelocKind, marker SyncMarker) Encoder {
|
|
||||||
e := pw.NewEncoderRaw(k)
|
|
||||||
e.Sync(marker)
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoderRaw returns an Encoder for a new element within the given
|
|
||||||
// section.
|
|
||||||
//
|
|
||||||
// Most callers should use NewEncoder instead.
|
|
||||||
func (pw *PkgEncoder) NewEncoderRaw(k RelocKind) Encoder {
|
|
||||||
idx := Index(len(pw.elems[k]))
|
|
||||||
pw.elems[k] = append(pw.elems[k], "") // placeholder
|
|
||||||
|
|
||||||
return Encoder{
|
|
||||||
p: pw,
|
|
||||||
k: k,
|
|
||||||
Idx: idx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An Encoder provides methods for encoding an individual element's
|
|
||||||
// bitstream data.
|
|
||||||
type Encoder struct {
|
|
||||||
p *PkgEncoder
|
|
||||||
|
|
||||||
Relocs []RelocEnt
|
|
||||||
RelocMap map[RelocEnt]uint32
|
|
||||||
Data bytes.Buffer // accumulated element bitstream data
|
|
||||||
|
|
||||||
encodingRelocHeader bool
|
|
||||||
|
|
||||||
k RelocKind
|
|
||||||
Idx Index // index within relocation section
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush finalizes the element's bitstream and returns its Index.
|
|
||||||
func (w *Encoder) Flush() Index {
|
|
||||||
var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved
|
|
||||||
|
|
||||||
// Backup the data so we write the relocations at the front.
|
|
||||||
var tmp bytes.Buffer
|
|
||||||
io.Copy(&tmp, &w.Data)
|
|
||||||
|
|
||||||
// TODO(mdempsky): Consider writing these out separately so they're
|
|
||||||
// easier to strip, along with function bodies, so that we can prune
|
|
||||||
// down to just the data that's relevant to go/types.
|
|
||||||
if w.encodingRelocHeader {
|
|
||||||
panic("encodingRelocHeader already true; recursive flush?")
|
|
||||||
}
|
|
||||||
w.encodingRelocHeader = true
|
|
||||||
w.Sync(SyncRelocs)
|
|
||||||
w.Len(len(w.Relocs))
|
|
||||||
for _, rEnt := range w.Relocs {
|
|
||||||
w.Sync(SyncReloc)
|
|
||||||
w.Len(int(rEnt.Kind))
|
|
||||||
w.Len(int(rEnt.Idx))
|
|
||||||
}
|
|
||||||
|
|
||||||
io.Copy(&sb, &w.Data)
|
|
||||||
io.Copy(&sb, &tmp)
|
|
||||||
w.p.elems[w.k][w.Idx] = sb.String()
|
|
||||||
|
|
||||||
return w.Idx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Encoder) checkErr(err error) {
|
|
||||||
if err != nil {
|
|
||||||
errorf("unexpected encoding error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Encoder) rawUvarint(x uint64) {
|
|
||||||
var buf [binary.MaxVarintLen64]byte
|
|
||||||
n := binary.PutUvarint(buf[:], x)
|
|
||||||
_, err := w.Data.Write(buf[:n])
|
|
||||||
w.checkErr(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Encoder) rawVarint(x int64) {
|
|
||||||
// Zig-zag encode.
|
|
||||||
ux := uint64(x) << 1
|
|
||||||
if x < 0 {
|
|
||||||
ux = ^ux
|
|
||||||
}
|
|
||||||
|
|
||||||
w.rawUvarint(ux)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Encoder) rawReloc(r RelocKind, idx Index) int {
|
|
||||||
e := RelocEnt{r, idx}
|
|
||||||
if w.RelocMap != nil {
|
|
||||||
if i, ok := w.RelocMap[e]; ok {
|
|
||||||
return int(i)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
w.RelocMap = make(map[RelocEnt]uint32)
|
|
||||||
}
|
|
||||||
|
|
||||||
i := len(w.Relocs)
|
|
||||||
w.RelocMap[e] = uint32(i)
|
|
||||||
w.Relocs = append(w.Relocs, e)
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Encoder) Sync(m SyncMarker) {
|
|
||||||
if !w.p.SyncMarkers() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writing out stack frame string references requires working
|
|
||||||
// relocations, but writing out the relocations themselves involves
|
|
||||||
// sync markers. To prevent infinite recursion, we simply trim the
|
|
||||||
// stack frame for sync markers within the relocation header.
|
|
||||||
var frames []string
|
|
||||||
if !w.encodingRelocHeader && w.p.syncFrames > 0 {
|
|
||||||
pcs := make([]uintptr, w.p.syncFrames)
|
|
||||||
n := runtime.Callers(2, pcs)
|
|
||||||
frames = fmtFrames(pcs[:n]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(mdempsky): Save space by writing out stack frames as a
|
|
||||||
// linked list so we can share common stack frames.
|
|
||||||
w.rawUvarint(uint64(m))
|
|
||||||
w.rawUvarint(uint64(len(frames)))
|
|
||||||
for _, frame := range frames {
|
|
||||||
w.rawUvarint(uint64(w.rawReloc(RelocString, w.p.StringIdx(frame))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool encodes and writes a bool value into the element bitstream,
|
|
||||||
// and then returns the bool value.
|
|
||||||
//
|
|
||||||
// For simple, 2-alternative encodings, the idiomatic way to call Bool
|
|
||||||
// is something like:
|
|
||||||
//
|
|
||||||
// if w.Bool(x != 0) {
|
|
||||||
// // alternative #1
|
|
||||||
// } else {
|
|
||||||
// // alternative #2
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// For multi-alternative encodings, use Code instead.
|
|
||||||
func (w *Encoder) Bool(b bool) bool {
|
|
||||||
w.Sync(SyncBool)
|
|
||||||
var x byte
|
|
||||||
if b {
|
|
||||||
x = 1
|
|
||||||
}
|
|
||||||
err := w.Data.WriteByte(x)
|
|
||||||
w.checkErr(err)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 encodes and writes an int64 value into the element bitstream.
|
|
||||||
func (w *Encoder) Int64(x int64) {
|
|
||||||
w.Sync(SyncInt64)
|
|
||||||
w.rawVarint(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64 encodes and writes a uint64 value into the element bitstream.
|
|
||||||
func (w *Encoder) Uint64(x uint64) {
|
|
||||||
w.Sync(SyncUint64)
|
|
||||||
w.rawUvarint(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len encodes and writes a non-negative int value into the element bitstream.
|
|
||||||
func (w *Encoder) Len(x int) { assert(x >= 0); w.Uint64(uint64(x)) }
|
|
||||||
|
|
||||||
// Int encodes and writes an int value into the element bitstream.
|
|
||||||
func (w *Encoder) Int(x int) { w.Int64(int64(x)) }
|
|
||||||
|
|
||||||
// Uint encodes and writes a uint value into the element bitstream.
|
|
||||||
func (w *Encoder) Uint(x uint) { w.Uint64(uint64(x)) }
|
|
||||||
|
|
||||||
// Reloc encodes and writes a relocation for the given (section,
|
|
||||||
// index) pair into the element bitstream.
|
|
||||||
//
|
|
||||||
// Note: Only the index is formally written into the element
|
|
||||||
// bitstream, so bitstream decoders must know from context which
|
|
||||||
// section an encoded relocation refers to.
|
|
||||||
func (w *Encoder) Reloc(r RelocKind, idx Index) {
|
|
||||||
w.Sync(SyncUseReloc)
|
|
||||||
w.Len(w.rawReloc(r, idx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Code encodes and writes a Code value into the element bitstream.
|
|
||||||
func (w *Encoder) Code(c Code) {
|
|
||||||
w.Sync(c.Marker())
|
|
||||||
w.Len(c.Value())
|
|
||||||
}
|
|
||||||
|
|
||||||
// String encodes and writes a string value into the element
|
|
||||||
// bitstream.
|
|
||||||
//
|
|
||||||
// Internally, strings are deduplicated by adding them to the strings
|
|
||||||
// section (if not already present), and then writing a relocation
|
|
||||||
// into the element bitstream.
|
|
||||||
func (w *Encoder) String(s string) {
|
|
||||||
w.Sync(SyncString)
|
|
||||||
w.Reloc(RelocString, w.p.StringIdx(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings encodes and writes a variable-length slice of strings into
|
|
||||||
// the element bitstream.
|
|
||||||
func (w *Encoder) Strings(ss []string) {
|
|
||||||
w.Len(len(ss))
|
|
||||||
for _, s := range ss {
|
|
||||||
w.String(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value encodes and writes a constant.Value into the element
|
|
||||||
// bitstream.
|
|
||||||
func (w *Encoder) Value(val constant.Value) {
|
|
||||||
w.Sync(SyncValue)
|
|
||||||
if w.Bool(val.Kind() == constant.Complex) {
|
|
||||||
w.scalar(constant.Real(val))
|
|
||||||
w.scalar(constant.Imag(val))
|
|
||||||
} else {
|
|
||||||
w.scalar(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Encoder) scalar(val constant.Value) {
|
|
||||||
switch v := constant.Val(val).(type) {
|
|
||||||
default:
|
|
||||||
errorf("unhandled %v (%v)", val, val.Kind())
|
|
||||||
case bool:
|
|
||||||
w.Code(ValBool)
|
|
||||||
w.Bool(v)
|
|
||||||
case string:
|
|
||||||
w.Code(ValString)
|
|
||||||
w.String(v)
|
|
||||||
case int64:
|
|
||||||
w.Code(ValInt64)
|
|
||||||
w.Int64(v)
|
|
||||||
case *big.Int:
|
|
||||||
w.Code(ValBigInt)
|
|
||||||
w.bigInt(v)
|
|
||||||
case *big.Rat:
|
|
||||||
w.Code(ValBigRat)
|
|
||||||
w.bigInt(v.Num())
|
|
||||||
w.bigInt(v.Denom())
|
|
||||||
case *big.Float:
|
|
||||||
w.Code(ValBigFloat)
|
|
||||||
w.bigFloat(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Encoder) bigInt(v *big.Int) {
|
|
||||||
b := v.Bytes()
|
|
||||||
w.String(string(b)) // TODO: More efficient encoding.
|
|
||||||
w.Bool(v.Sign() < 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Encoder) bigFloat(v *big.Float) {
|
|
||||||
b := v.Append(nil, 'p', -1)
|
|
||||||
w.String(string(b)) // TODO: More efficient encoding.
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
// Copyright 2022 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 pkgbits
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagSyncMarkers = 1 << iota // file format contains sync markers
|
|
||||||
)
|
|
|
@ -1,21 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build !go1.7
|
|
||||||
// +build !go1.7
|
|
||||||
|
|
||||||
// TODO(mdempsky): Remove after #44505 is resolved
|
|
||||||
|
|
||||||
package pkgbits
|
|
||||||
|
|
||||||
import "runtime"
|
|
||||||
|
|
||||||
func walkFrames(pcs []uintptr, visit frameVisitor) {
|
|
||||||
for _, pc := range pcs {
|
|
||||||
fn := runtime.FuncForPC(pc)
|
|
||||||
file, line := fn.FileLine(pc)
|
|
||||||
|
|
||||||
visit(file, line, fn.Name(), pc-fn.Entry())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build go1.7
|
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package pkgbits
|
|
||||||
|
|
||||||
import "runtime"
|
|
||||||
|
|
||||||
// walkFrames calls visit for each call frame represented by pcs.
|
|
||||||
//
|
|
||||||
// pcs should be a slice of PCs, as returned by runtime.Callers.
|
|
||||||
func walkFrames(pcs []uintptr, visit frameVisitor) {
|
|
||||||
if len(pcs) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
frames := runtime.CallersFrames(pcs)
|
|
||||||
for {
|
|
||||||
frame, more := frames.Next()
|
|
||||||
visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry)
|
|
||||||
if !more {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
// Copyright 2021 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 pkgbits
|
|
||||||
|
|
||||||
// A RelocKind indicates a particular section within a unified IR export.
|
|
||||||
type RelocKind int32
|
|
||||||
|
|
||||||
// An Index represents a bitstream element index within a particular
|
|
||||||
// section.
|
|
||||||
type Index int32
|
|
||||||
|
|
||||||
// A relocEnt (relocation entry) is an entry in an element's local
|
|
||||||
// reference table.
|
|
||||||
//
|
|
||||||
// TODO(mdempsky): Rename this too.
|
|
||||||
type RelocEnt struct {
|
|
||||||
Kind RelocKind
|
|
||||||
Idx Index
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reserved indices within the meta relocation section.
|
|
||||||
const (
|
|
||||||
PublicRootIdx Index = 0
|
|
||||||
PrivateRootIdx Index = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
RelocString RelocKind = iota
|
|
||||||
RelocMeta
|
|
||||||
RelocPosBase
|
|
||||||
RelocPkg
|
|
||||||
RelocName
|
|
||||||
RelocType
|
|
||||||
RelocObj
|
|
||||||
RelocObjExt
|
|
||||||
RelocObjDict
|
|
||||||
RelocBody
|
|
||||||
|
|
||||||
numRelocs = iota
|
|
||||||
)
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright 2022 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 pkgbits
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func assert(b bool) {
|
|
||||||
if !b {
|
|
||||||
panic("assertion failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorf(format string, args ...interface{}) {
|
|
||||||
panic(fmt.Errorf(format, args...))
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
// Copyright 2021 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 pkgbits
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fmtFrames formats a backtrace for reporting reader/writer desyncs.
|
|
||||||
func fmtFrames(pcs ...uintptr) []string {
|
|
||||||
res := make([]string, 0, len(pcs))
|
|
||||||
walkFrames(pcs, func(file string, line int, name string, offset uintptr) {
|
|
||||||
// Trim package from function name. It's just redundant noise.
|
|
||||||
name = strings.TrimPrefix(name, "cmd/compile/internal/noder.")
|
|
||||||
|
|
||||||
res = append(res, fmt.Sprintf("%s:%v: %s +0x%v", file, line, name, offset))
|
|
||||||
})
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
type frameVisitor func(file string, line int, name string, offset uintptr)
|
|
||||||
|
|
||||||
// SyncMarker is an enum type that represents markers that may be
|
|
||||||
// written to export data to ensure the reader and writer stay
|
|
||||||
// synchronized.
|
|
||||||
type SyncMarker int
|
|
||||||
|
|
||||||
//go:generate stringer -type=SyncMarker -trimprefix=Sync
|
|
||||||
|
|
||||||
const (
|
|
||||||
_ SyncMarker = iota
|
|
||||||
|
|
||||||
// Public markers (known to go/types importers).
|
|
||||||
|
|
||||||
// Low-level coding markers.
|
|
||||||
SyncEOF
|
|
||||||
SyncBool
|
|
||||||
SyncInt64
|
|
||||||
SyncUint64
|
|
||||||
SyncString
|
|
||||||
SyncValue
|
|
||||||
SyncVal
|
|
||||||
SyncRelocs
|
|
||||||
SyncReloc
|
|
||||||
SyncUseReloc
|
|
||||||
|
|
||||||
// Higher-level object and type markers.
|
|
||||||
SyncPublic
|
|
||||||
SyncPos
|
|
||||||
SyncPosBase
|
|
||||||
SyncObject
|
|
||||||
SyncObject1
|
|
||||||
SyncPkg
|
|
||||||
SyncPkgDef
|
|
||||||
SyncMethod
|
|
||||||
SyncType
|
|
||||||
SyncTypeIdx
|
|
||||||
SyncTypeParamNames
|
|
||||||
SyncSignature
|
|
||||||
SyncParams
|
|
||||||
SyncParam
|
|
||||||
SyncCodeObj
|
|
||||||
SyncSym
|
|
||||||
SyncLocalIdent
|
|
||||||
SyncSelector
|
|
||||||
|
|
||||||
// Private markers (only known to cmd/compile).
|
|
||||||
SyncPrivate
|
|
||||||
|
|
||||||
SyncFuncExt
|
|
||||||
SyncVarExt
|
|
||||||
SyncTypeExt
|
|
||||||
SyncPragma
|
|
||||||
|
|
||||||
SyncExprList
|
|
||||||
SyncExprs
|
|
||||||
SyncExpr
|
|
||||||
SyncExprType
|
|
||||||
SyncAssign
|
|
||||||
SyncOp
|
|
||||||
SyncFuncLit
|
|
||||||
SyncCompLit
|
|
||||||
|
|
||||||
SyncDecl
|
|
||||||
SyncFuncBody
|
|
||||||
SyncOpenScope
|
|
||||||
SyncCloseScope
|
|
||||||
SyncCloseAnotherScope
|
|
||||||
SyncDeclNames
|
|
||||||
SyncDeclName
|
|
||||||
|
|
||||||
SyncStmts
|
|
||||||
SyncBlockStmt
|
|
||||||
SyncIfStmt
|
|
||||||
SyncForStmt
|
|
||||||
SyncSwitchStmt
|
|
||||||
SyncRangeStmt
|
|
||||||
SyncCaseClause
|
|
||||||
SyncCommClause
|
|
||||||
SyncSelectStmt
|
|
||||||
SyncDecls
|
|
||||||
SyncLabeledStmt
|
|
||||||
SyncUseObjLocal
|
|
||||||
SyncAddLocal
|
|
||||||
SyncLinkname
|
|
||||||
SyncStmt1
|
|
||||||
SyncStmtsEnd
|
|
||||||
SyncLabel
|
|
||||||
SyncOptLabel
|
|
||||||
)
|
|
|
@ -1,89 +0,0 @@
|
||||||
// Code generated by "stringer -type=SyncMarker -trimprefix=Sync"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package pkgbits
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[SyncEOF-1]
|
|
||||||
_ = x[SyncBool-2]
|
|
||||||
_ = x[SyncInt64-3]
|
|
||||||
_ = x[SyncUint64-4]
|
|
||||||
_ = x[SyncString-5]
|
|
||||||
_ = x[SyncValue-6]
|
|
||||||
_ = x[SyncVal-7]
|
|
||||||
_ = x[SyncRelocs-8]
|
|
||||||
_ = x[SyncReloc-9]
|
|
||||||
_ = x[SyncUseReloc-10]
|
|
||||||
_ = x[SyncPublic-11]
|
|
||||||
_ = x[SyncPos-12]
|
|
||||||
_ = x[SyncPosBase-13]
|
|
||||||
_ = x[SyncObject-14]
|
|
||||||
_ = x[SyncObject1-15]
|
|
||||||
_ = x[SyncPkg-16]
|
|
||||||
_ = x[SyncPkgDef-17]
|
|
||||||
_ = x[SyncMethod-18]
|
|
||||||
_ = x[SyncType-19]
|
|
||||||
_ = x[SyncTypeIdx-20]
|
|
||||||
_ = x[SyncTypeParamNames-21]
|
|
||||||
_ = x[SyncSignature-22]
|
|
||||||
_ = x[SyncParams-23]
|
|
||||||
_ = x[SyncParam-24]
|
|
||||||
_ = x[SyncCodeObj-25]
|
|
||||||
_ = x[SyncSym-26]
|
|
||||||
_ = x[SyncLocalIdent-27]
|
|
||||||
_ = x[SyncSelector-28]
|
|
||||||
_ = x[SyncPrivate-29]
|
|
||||||
_ = x[SyncFuncExt-30]
|
|
||||||
_ = x[SyncVarExt-31]
|
|
||||||
_ = x[SyncTypeExt-32]
|
|
||||||
_ = x[SyncPragma-33]
|
|
||||||
_ = x[SyncExprList-34]
|
|
||||||
_ = x[SyncExprs-35]
|
|
||||||
_ = x[SyncExpr-36]
|
|
||||||
_ = x[SyncExprType-37]
|
|
||||||
_ = x[SyncAssign-38]
|
|
||||||
_ = x[SyncOp-39]
|
|
||||||
_ = x[SyncFuncLit-40]
|
|
||||||
_ = x[SyncCompLit-41]
|
|
||||||
_ = x[SyncDecl-42]
|
|
||||||
_ = x[SyncFuncBody-43]
|
|
||||||
_ = x[SyncOpenScope-44]
|
|
||||||
_ = x[SyncCloseScope-45]
|
|
||||||
_ = x[SyncCloseAnotherScope-46]
|
|
||||||
_ = x[SyncDeclNames-47]
|
|
||||||
_ = x[SyncDeclName-48]
|
|
||||||
_ = x[SyncStmts-49]
|
|
||||||
_ = x[SyncBlockStmt-50]
|
|
||||||
_ = x[SyncIfStmt-51]
|
|
||||||
_ = x[SyncForStmt-52]
|
|
||||||
_ = x[SyncSwitchStmt-53]
|
|
||||||
_ = x[SyncRangeStmt-54]
|
|
||||||
_ = x[SyncCaseClause-55]
|
|
||||||
_ = x[SyncCommClause-56]
|
|
||||||
_ = x[SyncSelectStmt-57]
|
|
||||||
_ = x[SyncDecls-58]
|
|
||||||
_ = x[SyncLabeledStmt-59]
|
|
||||||
_ = x[SyncUseObjLocal-60]
|
|
||||||
_ = x[SyncAddLocal-61]
|
|
||||||
_ = x[SyncLinkname-62]
|
|
||||||
_ = x[SyncStmt1-63]
|
|
||||||
_ = x[SyncStmtsEnd-64]
|
|
||||||
_ = x[SyncLabel-65]
|
|
||||||
_ = x[SyncOptLabel-66]
|
|
||||||
}
|
|
||||||
|
|
||||||
const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel"
|
|
||||||
|
|
||||||
var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458}
|
|
||||||
|
|
||||||
func (i SyncMarker) String() string {
|
|
||||||
i -= 1
|
|
||||||
if i < 0 || i >= SyncMarker(len(_SyncMarker_index)-1) {
|
|
||||||
return "SyncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
|
||||||
}
|
|
||||||
return _SyncMarker_name[_SyncMarker_index[i]:_SyncMarker_index[i+1]]
|
|
||||||
}
|
|
|
@ -1,151 +0,0 @@
|
||||||
// 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 tokeninternal provides access to some internal features of the token
|
|
||||||
// package.
|
|
||||||
package tokeninternal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/token"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetLines returns the table of line-start offsets from a token.File.
|
|
||||||
func GetLines(file *token.File) []int {
|
|
||||||
// token.File has a Lines method on Go 1.21 and later.
|
|
||||||
if file, ok := (interface{})(file).(interface{ Lines() []int }); ok {
|
|
||||||
return file.Lines()
|
|
||||||
}
|
|
||||||
|
|
||||||
// This declaration must match that of token.File.
|
|
||||||
// This creates a risk of dependency skew.
|
|
||||||
// For now we check that the size of the two
|
|
||||||
// declarations is the same, on the (fragile) assumption
|
|
||||||
// that future changes would add fields.
|
|
||||||
type tokenFile119 struct {
|
|
||||||
_ string
|
|
||||||
_ int
|
|
||||||
_ int
|
|
||||||
mu sync.Mutex // we're not complete monsters
|
|
||||||
lines []int
|
|
||||||
_ []struct{}
|
|
||||||
}
|
|
||||||
type tokenFile118 struct {
|
|
||||||
_ *token.FileSet // deleted in go1.19
|
|
||||||
tokenFile119
|
|
||||||
}
|
|
||||||
|
|
||||||
type uP = unsafe.Pointer
|
|
||||||
switch unsafe.Sizeof(*file) {
|
|
||||||
case unsafe.Sizeof(tokenFile118{}):
|
|
||||||
var ptr *tokenFile118
|
|
||||||
*(*uP)(uP(&ptr)) = uP(file)
|
|
||||||
ptr.mu.Lock()
|
|
||||||
defer ptr.mu.Unlock()
|
|
||||||
return ptr.lines
|
|
||||||
|
|
||||||
case unsafe.Sizeof(tokenFile119{}):
|
|
||||||
var ptr *tokenFile119
|
|
||||||
*(*uP)(uP(&ptr)) = uP(file)
|
|
||||||
ptr.mu.Lock()
|
|
||||||
defer ptr.mu.Unlock()
|
|
||||||
return ptr.lines
|
|
||||||
|
|
||||||
default:
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,204 +0,0 @@
|
||||||
// Copyright 2021 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 typeparams contains common utilities for writing tools that interact
|
|
||||||
// with generic Go code, as introduced with Go 1.18.
|
|
||||||
//
|
|
||||||
// Many of the types and functions in this package are proxies for the new APIs
|
|
||||||
// introduced in the standard library with Go 1.18. For example, the
|
|
||||||
// typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec
|
|
||||||
// function returns the value of the go/ast.TypeSpec.TypeParams field. At Go
|
|
||||||
// versions older than 1.18 these helpers are implemented as stubs, allowing
|
|
||||||
// users of this package to write code that handles generic constructs inline,
|
|
||||||
// even if the Go version being used to compile does not support generics.
|
|
||||||
//
|
|
||||||
// Additionally, this package contains common utilities for working with the
|
|
||||||
// new generic constructs, to supplement the standard library APIs. Notably,
|
|
||||||
// the StructuralTerms API computes a minimal representation of the structural
|
|
||||||
// restrictions on a type parameter.
|
|
||||||
//
|
|
||||||
// An external version of these APIs is available in the
|
|
||||||
// golang.org/x/exp/typeparams module.
|
|
||||||
package typeparams
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/ast"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UnpackIndexExpr extracts data from AST nodes that represent index
|
|
||||||
// expressions.
|
|
||||||
//
|
|
||||||
// For an ast.IndexExpr, the resulting indices slice will contain exactly one
|
|
||||||
// index expression. For an ast.IndexListExpr (go1.18+), it may have a variable
|
|
||||||
// number of index expressions.
|
|
||||||
//
|
|
||||||
// For nodes that don't represent index expressions, the first return value of
|
|
||||||
// UnpackIndexExpr will be nil.
|
|
||||||
func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) {
|
|
||||||
switch e := n.(type) {
|
|
||||||
case *ast.IndexExpr:
|
|
||||||
return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack
|
|
||||||
case *IndexListExpr:
|
|
||||||
return e.X, e.Lbrack, e.Indices, e.Rbrack
|
|
||||||
}
|
|
||||||
return nil, token.NoPos, nil, token.NoPos
|
|
||||||
}
|
|
||||||
|
|
||||||
// PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on
|
|
||||||
// the cardinality of indices. Calling PackIndexExpr with len(indices) == 0
|
|
||||||
// will panic.
|
|
||||||
func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr {
|
|
||||||
switch len(indices) {
|
|
||||||
case 0:
|
|
||||||
panic("empty indices")
|
|
||||||
case 1:
|
|
||||||
return &ast.IndexExpr{
|
|
||||||
X: x,
|
|
||||||
Lbrack: lbrack,
|
|
||||||
Index: indices[0],
|
|
||||||
Rbrack: rbrack,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return &IndexListExpr{
|
|
||||||
X: x,
|
|
||||||
Lbrack: lbrack,
|
|
||||||
Indices: indices,
|
|
||||||
Rbrack: rbrack,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTypeParam reports whether t is a type parameter.
|
|
||||||
func IsTypeParam(t types.Type) bool {
|
|
||||||
_, ok := t.(*TypeParam)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// OriginMethod returns the origin method associated with the method fn.
|
|
||||||
// For methods on a non-generic receiver base type, this is just
|
|
||||||
// fn. However, for methods with a generic receiver, OriginMethod returns the
|
|
||||||
// corresponding method in the method set of the origin type.
|
|
||||||
//
|
|
||||||
// As a special case, if fn is not a method (has no receiver), OriginMethod
|
|
||||||
// returns fn.
|
|
||||||
func OriginMethod(fn *types.Func) *types.Func {
|
|
||||||
recv := fn.Type().(*types.Signature).Recv()
|
|
||||||
if recv == nil {
|
|
||||||
return fn
|
|
||||||
}
|
|
||||||
base := recv.Type()
|
|
||||||
p, isPtr := base.(*types.Pointer)
|
|
||||||
if isPtr {
|
|
||||||
base = p.Elem()
|
|
||||||
}
|
|
||||||
named, isNamed := base.(*types.Named)
|
|
||||||
if !isNamed {
|
|
||||||
// Receiver is a *types.Interface.
|
|
||||||
return fn
|
|
||||||
}
|
|
||||||
if ForNamed(named).Len() == 0 {
|
|
||||||
// Receiver base has no type parameters, so we can avoid the lookup below.
|
|
||||||
return fn
|
|
||||||
}
|
|
||||||
orig := NamedTypeOrigin(named)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenericAssignableTo is a generalization of types.AssignableTo that
|
|
||||||
// implements the following rule for uninstantiated generic types:
|
|
||||||
//
|
|
||||||
// If V and T are generic named types, then V is considered assignable to T if,
|
|
||||||
// for every possible instantation of V[A_1, ..., A_N], the instantiation
|
|
||||||
// T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N].
|
|
||||||
//
|
|
||||||
// If T has structural constraints, they must be satisfied by V.
|
|
||||||
//
|
|
||||||
// For example, consider the following type declarations:
|
|
||||||
//
|
|
||||||
// type Interface[T any] interface {
|
|
||||||
// Accept(T)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// type Container[T any] struct {
|
|
||||||
// Element T
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func (c Container[T]) Accept(t T) { c.Element = t }
|
|
||||||
//
|
|
||||||
// In this case, GenericAssignableTo reports that instantiations of Container
|
|
||||||
// are assignable to the corresponding instantiation of Interface.
|
|
||||||
func GenericAssignableTo(ctxt *Context, V, T types.Type) bool {
|
|
||||||
// If V and T are not both named, or do not have matching non-empty type
|
|
||||||
// parameter lists, fall back on types.AssignableTo.
|
|
||||||
|
|
||||||
VN, Vnamed := V.(*types.Named)
|
|
||||||
TN, Tnamed := T.(*types.Named)
|
|
||||||
if !Vnamed || !Tnamed {
|
|
||||||
return types.AssignableTo(V, T)
|
|
||||||
}
|
|
||||||
|
|
||||||
vtparams := ForNamed(VN)
|
|
||||||
ttparams := ForNamed(TN)
|
|
||||||
if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 {
|
|
||||||
return types.AssignableTo(V, T)
|
|
||||||
}
|
|
||||||
|
|
||||||
// V and T have the same (non-zero) number of type params. Instantiate both
|
|
||||||
// with the type parameters of V. This must always succeed for V, and will
|
|
||||||
// succeed for T if and only if the type set of each type parameter of V is a
|
|
||||||
// subset of the type set of the corresponding type parameter of T, meaning
|
|
||||||
// that every instantiation of V corresponds to a valid instantiation of T.
|
|
||||||
|
|
||||||
// Minor optimization: ensure we share a context across the two
|
|
||||||
// instantiations below.
|
|
||||||
if ctxt == nil {
|
|
||||||
ctxt = NewContext()
|
|
||||||
}
|
|
||||||
|
|
||||||
var targs []types.Type
|
|
||||||
for i := 0; i < vtparams.Len(); i++ {
|
|
||||||
targs = append(targs, vtparams.At(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
vinst, err := Instantiate(ctxt, V, targs, true)
|
|
||||||
if err != nil {
|
|
||||||
panic("type parameters should satisfy their own constraints")
|
|
||||||
}
|
|
||||||
|
|
||||||
tinst, err := Instantiate(ctxt, T, targs, true)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.AssignableTo(vinst, tinst)
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
// Copyright 2022 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 typeparams
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CoreType returns the core type of T or nil if T does not have a core type.
|
|
||||||
//
|
|
||||||
// See https://go.dev/ref/spec#Core_types for the definition of a core type.
|
|
||||||
func CoreType(T types.Type) types.Type {
|
|
||||||
U := T.Underlying()
|
|
||||||
if _, ok := U.(*types.Interface); !ok {
|
|
||||||
return U // for non-interface types,
|
|
||||||
}
|
|
||||||
|
|
||||||
terms, err := _NormalTerms(U)
|
|
||||||
if len(terms) == 0 || err != nil {
|
|
||||||
// len(terms) -> empty type set of interface.
|
|
||||||
// err != nil => U is invalid, exceeds complexity bounds, or has an empty type set.
|
|
||||||
return nil // no core type.
|
|
||||||
}
|
|
||||||
|
|
||||||
U = terms[0].Type().Underlying()
|
|
||||||
var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying())
|
|
||||||
for identical = 1; identical < len(terms); identical++ {
|
|
||||||
if !types.Identical(U, terms[identical].Type().Underlying()) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if identical == len(terms) {
|
|
||||||
// https://go.dev/ref/spec#Core_types
|
|
||||||
// "There is a single type U which is the underlying type of all types in the type set of T"
|
|
||||||
return U
|
|
||||||
}
|
|
||||||
ch, ok := U.(*types.Chan)
|
|
||||||
if !ok {
|
|
||||||
return nil // no core type as identical < len(terms) and U is not a channel.
|
|
||||||
}
|
|
||||||
// https://go.dev/ref/spec#Core_types
|
|
||||||
// "the type chan E if T contains only bidirectional channels, or the type chan<- E or
|
|
||||||
// <-chan E depending on the direction of the directional channels present."
|
|
||||||
for chans := identical; chans < len(terms); chans++ {
|
|
||||||
curr, ok := terms[chans].Type().Underlying().(*types.Chan)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !types.Identical(ch.Elem(), curr.Elem()) {
|
|
||||||
return nil // channel elements are not identical.
|
|
||||||
}
|
|
||||||
if ch.Dir() == types.SendRecv {
|
|
||||||
// ch is bidirectional. We can safely always use curr's direction.
|
|
||||||
ch = curr
|
|
||||||
} else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() {
|
|
||||||
// ch and curr are not bidirectional and not the same direction.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
|
|
||||||
// _NormalTerms returns a slice of terms representing the normalized structural
|
|
||||||
// type restrictions of a type, if any.
|
|
||||||
//
|
|
||||||
// For all types other than *types.TypeParam, *types.Interface, and
|
|
||||||
// *types.Union, this is just a single term with Tilde() == false and
|
|
||||||
// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see
|
|
||||||
// below.
|
|
||||||
//
|
|
||||||
// Structural type restrictions of a type parameter are created via
|
|
||||||
// non-interface types embedded in its constraint interface (directly, or via a
|
|
||||||
// chain of interface embeddings). For example, in the declaration type
|
|
||||||
// T[P interface{~int; m()}] int the structural restriction of the type
|
|
||||||
// parameter P is ~int.
|
|
||||||
//
|
|
||||||
// With interface embedding and unions, the specification of structural type
|
|
||||||
// restrictions may be arbitrarily complex. For example, consider the
|
|
||||||
// following:
|
|
||||||
//
|
|
||||||
// type A interface{ ~string|~[]byte }
|
|
||||||
//
|
|
||||||
// type B interface{ int|string }
|
|
||||||
//
|
|
||||||
// type C interface { ~string|~int }
|
|
||||||
//
|
|
||||||
// type T[P interface{ A|B; C }] int
|
|
||||||
//
|
|
||||||
// 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,
|
|
||||||
// which when intersected with C (~string|~int) yields ~string|int.
|
|
||||||
//
|
|
||||||
// _NormalTerms computes these expansions and reductions, producing a
|
|
||||||
// "normalized" form of the embeddings. A structural restriction is normalized
|
|
||||||
// if it is a single union containing no interface terms, and is minimal in the
|
|
||||||
// sense that removing any term changes the set of types satisfying the
|
|
||||||
// constraint. It is left as a proof for the reader that, modulo sorting, there
|
|
||||||
// is exactly one such normalized form.
|
|
||||||
//
|
|
||||||
// Because the minimal representation always takes this form, _NormalTerms
|
|
||||||
// returns a slice of tilde terms corresponding to the terms of the union in
|
|
||||||
// the normalized structural restriction. An error is returned if the type is
|
|
||||||
// invalid, exceeds complexity bounds, or has an empty type set. In the latter
|
|
||||||
// case, _NormalTerms returns ErrEmptyTypeSet.
|
|
||||||
//
|
|
||||||
// _NormalTerms makes no guarantees about the order of terms, except that it
|
|
||||||
// is deterministic.
|
|
||||||
func _NormalTerms(typ types.Type) ([]*Term, error) {
|
|
||||||
switch typ := typ.(type) {
|
|
||||||
case *TypeParam:
|
|
||||||
return StructuralTerms(typ)
|
|
||||||
case *Union:
|
|
||||||
return UnionTermSet(typ)
|
|
||||||
case *types.Interface:
|
|
||||||
return InterfaceTermSet(typ)
|
|
||||||
default:
|
|
||||||
return []*Term{NewTerm(false, typ)}, nil
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build !go1.18
|
|
||||||
// +build !go1.18
|
|
||||||
|
|
||||||
package typeparams
|
|
||||||
|
|
||||||
// Enabled reports whether type parameters are enabled in the current build
|
|
||||||
// environment.
|
|
||||||
const Enabled = false
|
|
|
@ -1,15 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build go1.18
|
|
||||||
// +build go1.18
|
|
||||||
|
|
||||||
package typeparams
|
|
||||||
|
|
||||||
// Note: this constant is in a separate file as this is the only acceptable
|
|
||||||
// diff between the <1.18 API of this package and the 1.18 API.
|
|
||||||
|
|
||||||
// Enabled reports whether type parameters are enabled in the current build
|
|
||||||
// environment.
|
|
||||||
const Enabled = true
|
|
|
@ -1,218 +0,0 @@
|
||||||
// Copyright 2021 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 typeparams
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"go/types"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate go run copytermlist.go
|
|
||||||
|
|
||||||
const debug = false
|
|
||||||
|
|
||||||
var ErrEmptyTypeSet = errors.New("empty type set")
|
|
||||||
|
|
||||||
// StructuralTerms returns a slice of terms representing the normalized
|
|
||||||
// structural type restrictions of a type parameter, if any.
|
|
||||||
//
|
|
||||||
// Structural type restrictions of a type parameter are created via
|
|
||||||
// non-interface types embedded in its constraint interface (directly, or via a
|
|
||||||
// chain of interface embeddings). For example, in the declaration
|
|
||||||
//
|
|
||||||
// type T[P interface{~int; m()}] int
|
|
||||||
//
|
|
||||||
// the structural restriction of the type parameter P is ~int.
|
|
||||||
//
|
|
||||||
// With interface embedding and unions, the specification of structural type
|
|
||||||
// restrictions may be arbitrarily complex. For example, consider the
|
|
||||||
// following:
|
|
||||||
//
|
|
||||||
// type A interface{ ~string|~[]byte }
|
|
||||||
//
|
|
||||||
// type B interface{ int|string }
|
|
||||||
//
|
|
||||||
// type C interface { ~string|~int }
|
|
||||||
//
|
|
||||||
// type T[P interface{ A|B; C }] int
|
|
||||||
//
|
|
||||||
// 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,
|
|
||||||
// which when intersected with C (~string|~int) yields ~string|int.
|
|
||||||
//
|
|
||||||
// StructuralTerms computes these expansions and reductions, producing a
|
|
||||||
// "normalized" form of the embeddings. A structural restriction is normalized
|
|
||||||
// if it is a single union containing no interface terms, and is minimal in the
|
|
||||||
// sense that removing any term changes the set of types satisfying the
|
|
||||||
// constraint. It is left as a proof for the reader that, modulo sorting, there
|
|
||||||
// is exactly one such normalized form.
|
|
||||||
//
|
|
||||||
// Because the minimal representation always takes this form, StructuralTerms
|
|
||||||
// returns a slice of tilde terms corresponding to the terms of the union in
|
|
||||||
// the normalized structural restriction. An error is returned if the
|
|
||||||
// constraint interface is invalid, exceeds complexity bounds, or has an empty
|
|
||||||
// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.
|
|
||||||
//
|
|
||||||
// StructuralTerms makes no guarantees about the order of terms, except that it
|
|
||||||
// is deterministic.
|
|
||||||
func StructuralTerms(tparam *TypeParam) ([]*Term, error) {
|
|
||||||
constraint := tparam.Constraint()
|
|
||||||
if constraint == nil {
|
|
||||||
return nil, fmt.Errorf("%s has nil constraint", tparam)
|
|
||||||
}
|
|
||||||
iface, _ := constraint.Underlying().(*types.Interface)
|
|
||||||
if iface == nil {
|
|
||||||
return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying())
|
|
||||||
}
|
|
||||||
return InterfaceTermSet(iface)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InterfaceTermSet computes the normalized terms for a constraint interface,
|
|
||||||
// returning an error if the term set cannot be computed or is empty. In the
|
|
||||||
// latter case, the error will be ErrEmptyTypeSet.
|
|
||||||
//
|
|
||||||
// See the documentation of StructuralTerms for more information on
|
|
||||||
// normalization.
|
|
||||||
func InterfaceTermSet(iface *types.Interface) ([]*Term, error) {
|
|
||||||
return computeTermSet(iface)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnionTermSet computes the normalized terms for a union, returning an error
|
|
||||||
// if the term set cannot be computed or is empty. In the latter case, the
|
|
||||||
// error will be ErrEmptyTypeSet.
|
|
||||||
//
|
|
||||||
// See the documentation of StructuralTerms for more information on
|
|
||||||
// normalization.
|
|
||||||
func UnionTermSet(union *Union) ([]*Term, error) {
|
|
||||||
return computeTermSet(union)
|
|
||||||
}
|
|
||||||
|
|
||||||
func computeTermSet(typ types.Type) ([]*Term, error) {
|
|
||||||
tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if tset.terms.isEmpty() {
|
|
||||||
return nil, ErrEmptyTypeSet
|
|
||||||
}
|
|
||||||
if tset.terms.isAll() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
var terms []*Term
|
|
||||||
for _, term := range tset.terms {
|
|
||||||
terms = append(terms, NewTerm(term.tilde, term.typ))
|
|
||||||
}
|
|
||||||
return terms, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A termSet holds the normalized set of terms for a given type.
|
|
||||||
//
|
|
||||||
// The name termSet is intentionally distinct from 'type set': a type set is
|
|
||||||
// all types that implement a type (and includes method restrictions), whereas
|
|
||||||
// a term set just represents the structural restrictions on a type.
|
|
||||||
type termSet struct {
|
|
||||||
complete bool
|
|
||||||
terms termlist
|
|
||||||
}
|
|
||||||
|
|
||||||
func indentf(depth int, format string, args ...interface{}) {
|
|
||||||
fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
|
|
||||||
if t == nil {
|
|
||||||
panic("nil type")
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug {
|
|
||||||
indentf(depth, "%s", t.String())
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
indentf(depth, "=> %s", err)
|
|
||||||
} else {
|
|
||||||
indentf(depth, "=> %s", res.terms.String())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxTermCount = 100
|
|
||||||
if tset, ok := seen[t]; ok {
|
|
||||||
if !tset.complete {
|
|
||||||
return nil, fmt.Errorf("cycle detected in the declaration of %s", t)
|
|
||||||
}
|
|
||||||
return tset, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the current type as seen to avoid infinite recursion.
|
|
||||||
tset := new(termSet)
|
|
||||||
defer func() {
|
|
||||||
tset.complete = true
|
|
||||||
}()
|
|
||||||
seen[t] = tset
|
|
||||||
|
|
||||||
switch u := t.Underlying().(type) {
|
|
||||||
case *types.Interface:
|
|
||||||
// The term set of an interface is the intersection of the term sets of its
|
|
||||||
// embedded types.
|
|
||||||
tset.terms = allTermlist
|
|
||||||
for i := 0; i < u.NumEmbeddeds(); i++ {
|
|
||||||
embedded := u.EmbeddedType(i)
|
|
||||||
if _, ok := embedded.Underlying().(*TypeParam); ok {
|
|
||||||
return nil, fmt.Errorf("invalid embedded type %T", embedded)
|
|
||||||
}
|
|
||||||
tset2, err := computeTermSetInternal(embedded, seen, depth+1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tset.terms = tset.terms.intersect(tset2.terms)
|
|
||||||
}
|
|
||||||
case *Union:
|
|
||||||
// The term set of a union is the union of term sets of its terms.
|
|
||||||
tset.terms = nil
|
|
||||||
for i := 0; i < u.Len(); i++ {
|
|
||||||
t := u.Term(i)
|
|
||||||
var terms termlist
|
|
||||||
switch t.Type().Underlying().(type) {
|
|
||||||
case *types.Interface:
|
|
||||||
tset2, err := computeTermSetInternal(t.Type(), seen, depth+1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
terms = tset2.terms
|
|
||||||
case *TypeParam, *Union:
|
|
||||||
// A stand-alone type parameter or union is not permitted as union
|
|
||||||
// term.
|
|
||||||
return nil, fmt.Errorf("invalid union term %T", t)
|
|
||||||
default:
|
|
||||||
if t.Type() == types.Typ[types.Invalid] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
terms = termlist{{t.Tilde(), t.Type()}}
|
|
||||||
}
|
|
||||||
tset.terms = tset.terms.union(terms)
|
|
||||||
if len(tset.terms) > maxTermCount {
|
|
||||||
return nil, fmt.Errorf("exceeded max term count %d", maxTermCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *TypeParam:
|
|
||||||
panic("unreachable")
|
|
||||||
default:
|
|
||||||
// For all other types, the term set is just a single non-tilde term
|
|
||||||
// holding the type itself.
|
|
||||||
if u != types.Typ[types.Invalid] {
|
|
||||||
tset.terms = termlist{{false, t}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tset, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// under is a facade for the go/types internal function of the same name. It is
|
|
||||||
// used by typeterm.go.
|
|
||||||
func under(t types.Type) types.Type {
|
|
||||||
return t.Underlying()
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
// Code generated by copytermlist.go DO NOT EDIT.
|
|
||||||
|
|
||||||
package typeparams
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A termlist represents the type set represented by the union
|
|
||||||
// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn.
|
|
||||||
// A termlist is in normal form if all terms are disjoint.
|
|
||||||
// termlist operations don't require the operands to be in
|
|
||||||
// normal form.
|
|
||||||
type termlist []*term
|
|
||||||
|
|
||||||
// allTermlist represents the set of all types.
|
|
||||||
// It is in normal form.
|
|
||||||
var allTermlist = termlist{new(term)}
|
|
||||||
|
|
||||||
// String prints the termlist exactly (without normalization).
|
|
||||||
func (xl termlist) String() string {
|
|
||||||
if len(xl) == 0 {
|
|
||||||
return "∅"
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for i, x := range xl {
|
|
||||||
if i > 0 {
|
|
||||||
buf.WriteString(" | ")
|
|
||||||
}
|
|
||||||
buf.WriteString(x.String())
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEmpty reports whether the termlist xl represents the empty set of types.
|
|
||||||
func (xl termlist) isEmpty() bool {
|
|
||||||
// If there's a non-nil term, the entire list is not empty.
|
|
||||||
// If the termlist is in normal form, this requires at most
|
|
||||||
// one iteration.
|
|
||||||
for _, x := range xl {
|
|
||||||
if x != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// isAll reports whether the termlist xl represents the set of all types.
|
|
||||||
func (xl termlist) isAll() bool {
|
|
||||||
// If there's a 𝓤 term, the entire list is 𝓤.
|
|
||||||
// If the termlist is in normal form, this requires at most
|
|
||||||
// one iteration.
|
|
||||||
for _, x := range xl {
|
|
||||||
if x != nil && x.typ == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// norm returns the normal form of xl.
|
|
||||||
func (xl termlist) norm() termlist {
|
|
||||||
// Quadratic algorithm, but good enough for now.
|
|
||||||
// TODO(gri) fix asymptotic performance
|
|
||||||
used := make([]bool, len(xl))
|
|
||||||
var rl termlist
|
|
||||||
for i, xi := range xl {
|
|
||||||
if xi == nil || used[i] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for j := i + 1; j < len(xl); j++ {
|
|
||||||
xj := xl[j]
|
|
||||||
if xj == nil || used[j] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if u1, u2 := xi.union(xj); u2 == nil {
|
|
||||||
// If we encounter a 𝓤 term, the entire list is 𝓤.
|
|
||||||
// Exit early.
|
|
||||||
// (Note that this is not just an optimization;
|
|
||||||
// if we continue, we may end up with a 𝓤 term
|
|
||||||
// and other terms and the result would not be
|
|
||||||
// in normal form.)
|
|
||||||
if u1.typ == nil {
|
|
||||||
return allTermlist
|
|
||||||
}
|
|
||||||
xi = u1
|
|
||||||
used[j] = true // xj is now unioned into xi - ignore it in future iterations
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rl = append(rl, xi)
|
|
||||||
}
|
|
||||||
return rl
|
|
||||||
}
|
|
||||||
|
|
||||||
// union returns the union xl ∪ yl.
|
|
||||||
func (xl termlist) union(yl termlist) termlist {
|
|
||||||
return append(xl, yl...).norm()
|
|
||||||
}
|
|
||||||
|
|
||||||
// intersect returns the intersection xl ∩ yl.
|
|
||||||
func (xl termlist) intersect(yl termlist) termlist {
|
|
||||||
if xl.isEmpty() || yl.isEmpty() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quadratic algorithm, but good enough for now.
|
|
||||||
// TODO(gri) fix asymptotic performance
|
|
||||||
var rl termlist
|
|
||||||
for _, x := range xl {
|
|
||||||
for _, y := range yl {
|
|
||||||
if r := x.intersect(y); r != nil {
|
|
||||||
rl = append(rl, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rl.norm()
|
|
||||||
}
|
|
||||||
|
|
||||||
// equal reports whether xl and yl represent the same type set.
|
|
||||||
func (xl termlist) equal(yl termlist) bool {
|
|
||||||
// TODO(gri) this should be more efficient
|
|
||||||
return xl.subsetOf(yl) && yl.subsetOf(xl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// includes reports whether t ∈ xl.
|
|
||||||
func (xl termlist) includes(t types.Type) bool {
|
|
||||||
for _, x := range xl {
|
|
||||||
if x.includes(t) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// supersetOf reports whether y ⊆ xl.
|
|
||||||
func (xl termlist) supersetOf(y *term) bool {
|
|
||||||
for _, x := range xl {
|
|
||||||
if y.subsetOf(x) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// subsetOf reports whether xl ⊆ yl.
|
|
||||||
func (xl termlist) subsetOf(yl termlist) bool {
|
|
||||||
if yl.isEmpty() {
|
|
||||||
return xl.isEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
// each term x of xl must be a subset of yl
|
|
||||||
for _, x := range xl {
|
|
||||||
if !yl.supersetOf(x) {
|
|
||||||
return false // x is not a subset yl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build !go1.18
|
|
||||||
// +build !go1.18
|
|
||||||
|
|
||||||
package typeparams
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func unsupported() {
|
|
||||||
panic("type parameters are unsupported at this go version")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexListExpr is a placeholder type, as type parameters are not supported at
|
|
||||||
// this Go version. Its methods panic on use.
|
|
||||||
type IndexListExpr struct {
|
|
||||||
ast.Expr
|
|
||||||
X ast.Expr // expression
|
|
||||||
Lbrack token.Pos // position of "["
|
|
||||||
Indices []ast.Expr // index expressions
|
|
||||||
Rbrack token.Pos // position of "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForTypeSpec returns an empty field list, as type parameters on not supported
|
|
||||||
// at this Go version.
|
|
||||||
func ForTypeSpec(*ast.TypeSpec) *ast.FieldList {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForFuncType returns an empty field list, as type parameters are not
|
|
||||||
// supported at this Go version.
|
|
||||||
func ForFuncType(*ast.FuncType) *ast.FieldList {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypeParam is a placeholder type, as type parameters are not supported at
|
|
||||||
// this Go version. Its methods panic on use.
|
|
||||||
type TypeParam struct{ types.Type }
|
|
||||||
|
|
||||||
func (*TypeParam) Index() int { unsupported(); return 0 }
|
|
||||||
func (*TypeParam) Constraint() types.Type { unsupported(); return nil }
|
|
||||||
func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil }
|
|
||||||
|
|
||||||
// TypeParamList is a placeholder for an empty type parameter list.
|
|
||||||
type TypeParamList struct{}
|
|
||||||
|
|
||||||
func (*TypeParamList) Len() int { return 0 }
|
|
||||||
func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil }
|
|
||||||
|
|
||||||
// TypeList is a placeholder for an empty type list.
|
|
||||||
type TypeList struct{}
|
|
||||||
|
|
||||||
func (*TypeList) Len() int { return 0 }
|
|
||||||
func (*TypeList) At(int) types.Type { unsupported(); return nil }
|
|
||||||
|
|
||||||
// NewTypeParam is unsupported at this Go version, and panics.
|
|
||||||
func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam {
|
|
||||||
unsupported()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTypeParamConstraint is unsupported at this Go version, and panics.
|
|
||||||
func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) {
|
|
||||||
unsupported()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSignatureType calls types.NewSignature, panicking if recvTypeParams or
|
|
||||||
// typeParams is non-empty.
|
|
||||||
func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature {
|
|
||||||
if len(recvTypeParams) != 0 || len(typeParams) != 0 {
|
|
||||||
panic("signatures cannot have type parameters at this Go version")
|
|
||||||
}
|
|
||||||
return types.NewSignature(recv, params, results, variadic)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForSignature returns an empty slice.
|
|
||||||
func ForSignature(*types.Signature) *TypeParamList {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecvTypeParams returns a nil slice.
|
|
||||||
func RecvTypeParams(sig *types.Signature) *TypeParamList {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsComparable returns false, as no interfaces are type-restricted at this Go
|
|
||||||
// version.
|
|
||||||
func IsComparable(*types.Interface) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsMethodSet returns true, as no interfaces are type-restricted at this Go
|
|
||||||
// version.
|
|
||||||
func IsMethodSet(*types.Interface) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsImplicit returns false, as no interfaces are implicit at this Go version.
|
|
||||||
func IsImplicit(*types.Interface) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkImplicit does nothing, because this Go version does not have implicit
|
|
||||||
// interfaces.
|
|
||||||
func MarkImplicit(*types.Interface) {}
|
|
||||||
|
|
||||||
// ForNamed returns an empty type parameter list, as type parameters are not
|
|
||||||
// supported at this Go version.
|
|
||||||
func ForNamed(*types.Named) *TypeParamList {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetForNamed panics if tparams is non-empty.
|
|
||||||
func SetForNamed(_ *types.Named, tparams []*TypeParam) {
|
|
||||||
if len(tparams) > 0 {
|
|
||||||
unsupported()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamedTypeArgs returns nil.
|
|
||||||
func NamedTypeArgs(*types.Named) *TypeList {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamedTypeOrigin is the identity method at this Go version.
|
|
||||||
func NamedTypeOrigin(named *types.Named) *types.Named {
|
|
||||||
return named
|
|
||||||
}
|
|
||||||
|
|
||||||
// Term holds information about a structural type restriction.
|
|
||||||
type Term struct {
|
|
||||||
tilde bool
|
|
||||||
typ types.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Term) Tilde() bool { return m.tilde }
|
|
||||||
func (m *Term) Type() types.Type { return m.typ }
|
|
||||||
func (m *Term) String() string {
|
|
||||||
pre := ""
|
|
||||||
if m.tilde {
|
|
||||||
pre = "~"
|
|
||||||
}
|
|
||||||
return pre + m.typ.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTerm is unsupported at this Go version, and panics.
|
|
||||||
func NewTerm(tilde bool, typ types.Type) *Term {
|
|
||||||
return &Term{tilde, typ}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Union is a placeholder type, as type parameters are not supported at this Go
|
|
||||||
// version. Its methods panic on use.
|
|
||||||
type Union struct{ types.Type }
|
|
||||||
|
|
||||||
func (*Union) Len() int { return 0 }
|
|
||||||
func (*Union) Term(i int) *Term { unsupported(); return nil }
|
|
||||||
|
|
||||||
// NewUnion is unsupported at this Go version, and panics.
|
|
||||||
func NewUnion(terms []*Term) *Union {
|
|
||||||
unsupported()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitInstanceInfo is a noop at this Go version.
|
|
||||||
func InitInstanceInfo(*types.Info) {}
|
|
||||||
|
|
||||||
// Instance is a placeholder type, as type parameters are not supported at this
|
|
||||||
// Go version.
|
|
||||||
type Instance struct {
|
|
||||||
TypeArgs *TypeList
|
|
||||||
Type types.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInstances returns a nil map, as type parameters are not supported at this
|
|
||||||
// Go version.
|
|
||||||
func GetInstances(info *types.Info) map[*ast.Ident]Instance { return nil }
|
|
||||||
|
|
||||||
// Context is a placeholder type, as type parameters are not supported at
|
|
||||||
// this Go version.
|
|
||||||
type Context struct{}
|
|
||||||
|
|
||||||
// NewContext returns a placeholder Context instance.
|
|
||||||
func NewContext() *Context {
|
|
||||||
return &Context{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instantiate is unsupported on this Go version, and panics.
|
|
||||||
func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) {
|
|
||||||
unsupported()
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
|
@ -1,151 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build go1.18
|
|
||||||
// +build go1.18
|
|
||||||
|
|
||||||
package typeparams
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IndexListExpr is an alias for ast.IndexListExpr.
|
|
||||||
type IndexListExpr = ast.IndexListExpr
|
|
||||||
|
|
||||||
// ForTypeSpec returns n.TypeParams.
|
|
||||||
func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList {
|
|
||||||
if n == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return n.TypeParams
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForFuncType returns n.TypeParams.
|
|
||||||
func ForFuncType(n *ast.FuncType) *ast.FieldList {
|
|
||||||
if n == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return n.TypeParams
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypeParam is an alias for types.TypeParam
|
|
||||||
type TypeParam = types.TypeParam
|
|
||||||
|
|
||||||
// TypeParamList is an alias for types.TypeParamList
|
|
||||||
type TypeParamList = types.TypeParamList
|
|
||||||
|
|
||||||
// TypeList is an alias for types.TypeList
|
|
||||||
type TypeList = types.TypeList
|
|
||||||
|
|
||||||
// NewTypeParam calls types.NewTypeParam.
|
|
||||||
func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam {
|
|
||||||
return types.NewTypeParam(name, constraint)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTypeParamConstraint calls tparam.SetConstraint(constraint).
|
|
||||||
func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) {
|
|
||||||
tparam.SetConstraint(constraint)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSignatureType calls types.NewSignatureType.
|
|
||||||
func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature {
|
|
||||||
return types.NewSignatureType(recv, recvTypeParams, typeParams, params, results, variadic)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForSignature returns sig.TypeParams()
|
|
||||||
func ForSignature(sig *types.Signature) *TypeParamList {
|
|
||||||
return sig.TypeParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecvTypeParams returns sig.RecvTypeParams().
|
|
||||||
func RecvTypeParams(sig *types.Signature) *TypeParamList {
|
|
||||||
return sig.RecvTypeParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsComparable calls iface.IsComparable().
|
|
||||||
func IsComparable(iface *types.Interface) bool {
|
|
||||||
return iface.IsComparable()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsMethodSet calls iface.IsMethodSet().
|
|
||||||
func IsMethodSet(iface *types.Interface) bool {
|
|
||||||
return iface.IsMethodSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsImplicit calls iface.IsImplicit().
|
|
||||||
func IsImplicit(iface *types.Interface) bool {
|
|
||||||
return iface.IsImplicit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkImplicit calls iface.MarkImplicit().
|
|
||||||
func MarkImplicit(iface *types.Interface) {
|
|
||||||
iface.MarkImplicit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForNamed extracts the (possibly empty) type parameter object list from
|
|
||||||
// named.
|
|
||||||
func ForNamed(named *types.Named) *TypeParamList {
|
|
||||||
return named.TypeParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetForNamed sets the type params tparams on n. Each tparam must be of
|
|
||||||
// dynamic type *types.TypeParam.
|
|
||||||
func SetForNamed(n *types.Named, tparams []*TypeParam) {
|
|
||||||
n.SetTypeParams(tparams)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamedTypeArgs returns named.TypeArgs().
|
|
||||||
func NamedTypeArgs(named *types.Named) *TypeList {
|
|
||||||
return named.TypeArgs()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamedTypeOrigin returns named.Orig().
|
|
||||||
func NamedTypeOrigin(named *types.Named) *types.Named {
|
|
||||||
return named.Origin()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Term is an alias for types.Term.
|
|
||||||
type Term = types.Term
|
|
||||||
|
|
||||||
// NewTerm calls types.NewTerm.
|
|
||||||
func NewTerm(tilde bool, typ types.Type) *Term {
|
|
||||||
return types.NewTerm(tilde, typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Union is an alias for types.Union
|
|
||||||
type Union = types.Union
|
|
||||||
|
|
||||||
// NewUnion calls types.NewUnion.
|
|
||||||
func NewUnion(terms []*Term) *Union {
|
|
||||||
return types.NewUnion(terms)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitInstanceInfo initializes info to record information about type and
|
|
||||||
// function instances.
|
|
||||||
func InitInstanceInfo(info *types.Info) {
|
|
||||||
info.Instances = make(map[*ast.Ident]types.Instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance is an alias for types.Instance.
|
|
||||||
type Instance = types.Instance
|
|
||||||
|
|
||||||
// GetInstances returns info.Instances.
|
|
||||||
func GetInstances(info *types.Info) map[*ast.Ident]Instance {
|
|
||||||
return info.Instances
|
|
||||||
}
|
|
||||||
|
|
||||||
// Context is an alias for types.Context.
|
|
||||||
type Context = types.Context
|
|
||||||
|
|
||||||
// NewContext calls types.NewContext.
|
|
||||||
func NewContext() *Context {
|
|
||||||
return types.NewContext()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instantiate calls types.Instantiate.
|
|
||||||
func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) {
|
|
||||||
return types.Instantiate(ctxt, typ, targs, validate)
|
|
||||||
}
|
|
|
@ -1,169 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
// Code generated by copytermlist.go DO NOT EDIT.
|
|
||||||
|
|
||||||
package typeparams
|
|
||||||
|
|
||||||
import "go/types"
|
|
||||||
|
|
||||||
// A term describes elementary type sets:
|
|
||||||
//
|
|
||||||
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
|
|
||||||
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
|
|
||||||
// T: &term{false, T} == {T} // set of type T
|
|
||||||
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
|
|
||||||
type term struct {
|
|
||||||
tilde bool // valid if typ != nil
|
|
||||||
typ types.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *term) String() string {
|
|
||||||
switch {
|
|
||||||
case x == nil:
|
|
||||||
return "∅"
|
|
||||||
case x.typ == nil:
|
|
||||||
return "𝓤"
|
|
||||||
case x.tilde:
|
|
||||||
return "~" + x.typ.String()
|
|
||||||
default:
|
|
||||||
return x.typ.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// equal reports whether x and y represent the same type set.
|
|
||||||
func (x *term) equal(y *term) bool {
|
|
||||||
// easy cases
|
|
||||||
switch {
|
|
||||||
case x == nil || y == nil:
|
|
||||||
return x == y
|
|
||||||
case x.typ == nil || y.typ == nil:
|
|
||||||
return x.typ == y.typ
|
|
||||||
}
|
|
||||||
// ∅ ⊂ x, y ⊂ 𝓤
|
|
||||||
|
|
||||||
return x.tilde == y.tilde && types.Identical(x.typ, y.typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
// union returns the union x ∪ y: zero, one, or two non-nil terms.
|
|
||||||
func (x *term) union(y *term) (_, _ *term) {
|
|
||||||
// easy cases
|
|
||||||
switch {
|
|
||||||
case x == nil && y == nil:
|
|
||||||
return nil, nil // ∅ ∪ ∅ == ∅
|
|
||||||
case x == nil:
|
|
||||||
return y, nil // ∅ ∪ y == y
|
|
||||||
case y == nil:
|
|
||||||
return x, nil // x ∪ ∅ == x
|
|
||||||
case x.typ == nil:
|
|
||||||
return x, nil // 𝓤 ∪ y == 𝓤
|
|
||||||
case y.typ == nil:
|
|
||||||
return y, nil // x ∪ 𝓤 == 𝓤
|
|
||||||
}
|
|
||||||
// ∅ ⊂ x, y ⊂ 𝓤
|
|
||||||
|
|
||||||
if x.disjoint(y) {
|
|
||||||
return x, y // x ∪ y == (x, y) if x ∩ y == ∅
|
|
||||||
}
|
|
||||||
// x.typ == y.typ
|
|
||||||
|
|
||||||
// ~t ∪ ~t == ~t
|
|
||||||
// ~t ∪ T == ~t
|
|
||||||
// T ∪ ~t == ~t
|
|
||||||
// T ∪ T == T
|
|
||||||
if x.tilde || !y.tilde {
|
|
||||||
return x, nil
|
|
||||||
}
|
|
||||||
return y, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// intersect returns the intersection x ∩ y.
|
|
||||||
func (x *term) intersect(y *term) *term {
|
|
||||||
// easy cases
|
|
||||||
switch {
|
|
||||||
case x == nil || y == nil:
|
|
||||||
return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅
|
|
||||||
case x.typ == nil:
|
|
||||||
return y // 𝓤 ∩ y == y
|
|
||||||
case y.typ == nil:
|
|
||||||
return x // x ∩ 𝓤 == x
|
|
||||||
}
|
|
||||||
// ∅ ⊂ x, y ⊂ 𝓤
|
|
||||||
|
|
||||||
if x.disjoint(y) {
|
|
||||||
return nil // x ∩ y == ∅ if x ∩ y == ∅
|
|
||||||
}
|
|
||||||
// x.typ == y.typ
|
|
||||||
|
|
||||||
// ~t ∩ ~t == ~t
|
|
||||||
// ~t ∩ T == T
|
|
||||||
// T ∩ ~t == T
|
|
||||||
// T ∩ T == T
|
|
||||||
if !x.tilde || y.tilde {
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
|
|
||||||
// includes reports whether t ∈ x.
|
|
||||||
func (x *term) includes(t types.Type) bool {
|
|
||||||
// easy cases
|
|
||||||
switch {
|
|
||||||
case x == nil:
|
|
||||||
return false // t ∈ ∅ == false
|
|
||||||
case x.typ == nil:
|
|
||||||
return true // t ∈ 𝓤 == true
|
|
||||||
}
|
|
||||||
// ∅ ⊂ x ⊂ 𝓤
|
|
||||||
|
|
||||||
u := t
|
|
||||||
if x.tilde {
|
|
||||||
u = under(u)
|
|
||||||
}
|
|
||||||
return types.Identical(x.typ, u)
|
|
||||||
}
|
|
||||||
|
|
||||||
// subsetOf reports whether x ⊆ y.
|
|
||||||
func (x *term) subsetOf(y *term) bool {
|
|
||||||
// easy cases
|
|
||||||
switch {
|
|
||||||
case x == nil:
|
|
||||||
return true // ∅ ⊆ y == true
|
|
||||||
case y == nil:
|
|
||||||
return false // x ⊆ ∅ == false since x != ∅
|
|
||||||
case y.typ == nil:
|
|
||||||
return true // x ⊆ 𝓤 == true
|
|
||||||
case x.typ == nil:
|
|
||||||
return false // 𝓤 ⊆ y == false since y != 𝓤
|
|
||||||
}
|
|
||||||
// ∅ ⊂ x, y ⊂ 𝓤
|
|
||||||
|
|
||||||
if x.disjoint(y) {
|
|
||||||
return false // x ⊆ y == false if x ∩ y == ∅
|
|
||||||
}
|
|
||||||
// x.typ == y.typ
|
|
||||||
|
|
||||||
// ~t ⊆ ~t == true
|
|
||||||
// ~t ⊆ T == false
|
|
||||||
// T ⊆ ~t == true
|
|
||||||
// T ⊆ T == true
|
|
||||||
return !x.tilde || y.tilde
|
|
||||||
}
|
|
||||||
|
|
||||||
// disjoint reports whether x ∩ y == ∅.
|
|
||||||
// x.typ and y.typ must not be nil.
|
|
||||||
func (x *term) disjoint(y *term) bool {
|
|
||||||
if debug && (x.typ == nil || y.typ == nil) {
|
|
||||||
panic("invalid argument(s)")
|
|
||||||
}
|
|
||||||
ux := x.typ
|
|
||||||
if y.tilde {
|
|
||||||
ux = under(ux)
|
|
||||||
}
|
|
||||||
uy := y.typ
|
|
||||||
if x.tilde {
|
|
||||||
uy = under(uy)
|
|
||||||
}
|
|
||||||
return !types.Identical(ux, uy)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,179 +0,0 @@
|
||||||
// Code generated by "stringer -type=ErrorCode"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package typesinternal
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[InvalidSyntaxTree - -1]
|
|
||||||
_ = x[Test-1]
|
|
||||||
_ = x[BlankPkgName-2]
|
|
||||||
_ = x[MismatchedPkgName-3]
|
|
||||||
_ = x[InvalidPkgUse-4]
|
|
||||||
_ = x[BadImportPath-5]
|
|
||||||
_ = x[BrokenImport-6]
|
|
||||||
_ = x[ImportCRenamed-7]
|
|
||||||
_ = x[UnusedImport-8]
|
|
||||||
_ = x[InvalidInitCycle-9]
|
|
||||||
_ = x[DuplicateDecl-10]
|
|
||||||
_ = x[InvalidDeclCycle-11]
|
|
||||||
_ = x[InvalidTypeCycle-12]
|
|
||||||
_ = x[InvalidConstInit-13]
|
|
||||||
_ = x[InvalidConstVal-14]
|
|
||||||
_ = x[InvalidConstType-15]
|
|
||||||
_ = x[UntypedNilUse-16]
|
|
||||||
_ = x[WrongAssignCount-17]
|
|
||||||
_ = x[UnassignableOperand-18]
|
|
||||||
_ = x[NoNewVar-19]
|
|
||||||
_ = x[MultiValAssignOp-20]
|
|
||||||
_ = x[InvalidIfaceAssign-21]
|
|
||||||
_ = x[InvalidChanAssign-22]
|
|
||||||
_ = x[IncompatibleAssign-23]
|
|
||||||
_ = x[UnaddressableFieldAssign-24]
|
|
||||||
_ = x[NotAType-25]
|
|
||||||
_ = x[InvalidArrayLen-26]
|
|
||||||
_ = x[BlankIfaceMethod-27]
|
|
||||||
_ = x[IncomparableMapKey-28]
|
|
||||||
_ = x[InvalidIfaceEmbed-29]
|
|
||||||
_ = x[InvalidPtrEmbed-30]
|
|
||||||
_ = x[BadRecv-31]
|
|
||||||
_ = x[InvalidRecv-32]
|
|
||||||
_ = x[DuplicateFieldAndMethod-33]
|
|
||||||
_ = x[DuplicateMethod-34]
|
|
||||||
_ = x[InvalidBlank-35]
|
|
||||||
_ = x[InvalidIota-36]
|
|
||||||
_ = x[MissingInitBody-37]
|
|
||||||
_ = x[InvalidInitSig-38]
|
|
||||||
_ = x[InvalidInitDecl-39]
|
|
||||||
_ = x[InvalidMainDecl-40]
|
|
||||||
_ = x[TooManyValues-41]
|
|
||||||
_ = x[NotAnExpr-42]
|
|
||||||
_ = x[TruncatedFloat-43]
|
|
||||||
_ = x[NumericOverflow-44]
|
|
||||||
_ = x[UndefinedOp-45]
|
|
||||||
_ = x[MismatchedTypes-46]
|
|
||||||
_ = x[DivByZero-47]
|
|
||||||
_ = x[NonNumericIncDec-48]
|
|
||||||
_ = x[UnaddressableOperand-49]
|
|
||||||
_ = x[InvalidIndirection-50]
|
|
||||||
_ = x[NonIndexableOperand-51]
|
|
||||||
_ = x[InvalidIndex-52]
|
|
||||||
_ = x[SwappedSliceIndices-53]
|
|
||||||
_ = x[NonSliceableOperand-54]
|
|
||||||
_ = x[InvalidSliceExpr-55]
|
|
||||||
_ = x[InvalidShiftCount-56]
|
|
||||||
_ = x[InvalidShiftOperand-57]
|
|
||||||
_ = x[InvalidReceive-58]
|
|
||||||
_ = x[InvalidSend-59]
|
|
||||||
_ = x[DuplicateLitKey-60]
|
|
||||||
_ = x[MissingLitKey-61]
|
|
||||||
_ = x[InvalidLitIndex-62]
|
|
||||||
_ = x[OversizeArrayLit-63]
|
|
||||||
_ = x[MixedStructLit-64]
|
|
||||||
_ = x[InvalidStructLit-65]
|
|
||||||
_ = x[MissingLitField-66]
|
|
||||||
_ = x[DuplicateLitField-67]
|
|
||||||
_ = x[UnexportedLitField-68]
|
|
||||||
_ = x[InvalidLitField-69]
|
|
||||||
_ = x[UntypedLit-70]
|
|
||||||
_ = x[InvalidLit-71]
|
|
||||||
_ = x[AmbiguousSelector-72]
|
|
||||||
_ = x[UndeclaredImportedName-73]
|
|
||||||
_ = x[UnexportedName-74]
|
|
||||||
_ = x[UndeclaredName-75]
|
|
||||||
_ = x[MissingFieldOrMethod-76]
|
|
||||||
_ = x[BadDotDotDotSyntax-77]
|
|
||||||
_ = x[NonVariadicDotDotDot-78]
|
|
||||||
_ = x[MisplacedDotDotDot-79]
|
|
||||||
_ = x[InvalidDotDotDotOperand-80]
|
|
||||||
_ = x[InvalidDotDotDot-81]
|
|
||||||
_ = x[UncalledBuiltin-82]
|
|
||||||
_ = x[InvalidAppend-83]
|
|
||||||
_ = x[InvalidCap-84]
|
|
||||||
_ = x[InvalidClose-85]
|
|
||||||
_ = x[InvalidCopy-86]
|
|
||||||
_ = x[InvalidComplex-87]
|
|
||||||
_ = x[InvalidDelete-88]
|
|
||||||
_ = x[InvalidImag-89]
|
|
||||||
_ = x[InvalidLen-90]
|
|
||||||
_ = x[SwappedMakeArgs-91]
|
|
||||||
_ = x[InvalidMake-92]
|
|
||||||
_ = x[InvalidReal-93]
|
|
||||||
_ = x[InvalidAssert-94]
|
|
||||||
_ = x[ImpossibleAssert-95]
|
|
||||||
_ = x[InvalidConversion-96]
|
|
||||||
_ = x[InvalidUntypedConversion-97]
|
|
||||||
_ = x[BadOffsetofSyntax-98]
|
|
||||||
_ = x[InvalidOffsetof-99]
|
|
||||||
_ = x[UnusedExpr-100]
|
|
||||||
_ = x[UnusedVar-101]
|
|
||||||
_ = x[MissingReturn-102]
|
|
||||||
_ = x[WrongResultCount-103]
|
|
||||||
_ = x[OutOfScopeResult-104]
|
|
||||||
_ = x[InvalidCond-105]
|
|
||||||
_ = x[InvalidPostDecl-106]
|
|
||||||
_ = x[InvalidChanRange-107]
|
|
||||||
_ = x[InvalidIterVar-108]
|
|
||||||
_ = x[InvalidRangeExpr-109]
|
|
||||||
_ = x[MisplacedBreak-110]
|
|
||||||
_ = x[MisplacedContinue-111]
|
|
||||||
_ = x[MisplacedFallthrough-112]
|
|
||||||
_ = x[DuplicateCase-113]
|
|
||||||
_ = x[DuplicateDefault-114]
|
|
||||||
_ = x[BadTypeKeyword-115]
|
|
||||||
_ = x[InvalidTypeSwitch-116]
|
|
||||||
_ = x[InvalidExprSwitch-117]
|
|
||||||
_ = x[InvalidSelectCase-118]
|
|
||||||
_ = x[UndeclaredLabel-119]
|
|
||||||
_ = x[DuplicateLabel-120]
|
|
||||||
_ = x[MisplacedLabel-121]
|
|
||||||
_ = x[UnusedLabel-122]
|
|
||||||
_ = x[JumpOverDecl-123]
|
|
||||||
_ = x[JumpIntoBlock-124]
|
|
||||||
_ = x[InvalidMethodExpr-125]
|
|
||||||
_ = x[WrongArgCount-126]
|
|
||||||
_ = x[InvalidCall-127]
|
|
||||||
_ = x[UnusedResults-128]
|
|
||||||
_ = x[InvalidDefer-129]
|
|
||||||
_ = x[InvalidGo-130]
|
|
||||||
_ = x[BadDecl-131]
|
|
||||||
_ = x[RepeatedDecl-132]
|
|
||||||
_ = x[InvalidUnsafeAdd-133]
|
|
||||||
_ = x[InvalidUnsafeSlice-134]
|
|
||||||
_ = x[UnsupportedFeature-135]
|
|
||||||
_ = x[NotAGenericType-136]
|
|
||||||
_ = x[WrongTypeArgCount-137]
|
|
||||||
_ = x[CannotInferTypeArgs-138]
|
|
||||||
_ = x[InvalidTypeArg-139]
|
|
||||||
_ = x[InvalidInstanceCycle-140]
|
|
||||||
_ = x[InvalidUnion-141]
|
|
||||||
_ = x[MisplacedConstraintIface-142]
|
|
||||||
_ = x[InvalidMethodTypeParams-143]
|
|
||||||
_ = x[MisplacedTypeParam-144]
|
|
||||||
_ = x[InvalidUnsafeSliceData-145]
|
|
||||||
_ = x[InvalidUnsafeString-146]
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
_ErrorCode_name_0 = "InvalidSyntaxTree"
|
|
||||||
_ErrorCode_name_1 = "TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKeyInvalidIfaceEmbedInvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDotInvalidDotDotDotOperandInvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDeclInvalidChanRangeInvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ErrorCode_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411, 428, 443, 450, 461, 484, 499, 511, 522, 537, 551, 566, 581, 594, 603, 617, 632, 643, 658, 667, 683, 703, 721, 740, 752, 771, 790, 806, 823, 842, 856, 867, 882, 895, 910, 926, 940, 956, 971, 988, 1006, 1021, 1031, 1041, 1058, 1080, 1094, 1108, 1128, 1146, 1166, 1184, 1207, 1223, 1238, 1251, 1261, 1273, 1284, 1298, 1311, 1322, 1332, 1347, 1358, 1369, 1382, 1398, 1415, 1439, 1456, 1471, 1481, 1490, 1503, 1519, 1535, 1546, 1561, 1577, 1591, 1607, 1621, 1638, 1658, 1671, 1687, 1701, 1718, 1735, 1752, 1767, 1781, 1795, 1806, 1818, 1831, 1848, 1861, 1872, 1885, 1897, 1906, 1913, 1925, 1941, 1959, 1977, 1992, 2009, 2028, 2042, 2062, 2074, 2098, 2121, 2139, 2161, 2180}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (i ErrorCode) String() string {
|
|
||||||
switch {
|
|
||||||
case i == -1:
|
|
||||||
return _ErrorCode_name_0
|
|
||||||
case 1 <= i && i <= 146:
|
|
||||||
i -= 1
|
|
||||||
return _ErrorCode_name_1[_ErrorCode_index_1[i]:_ErrorCode_index_1[i+1]]
|
|
||||||
default:
|
|
||||||
return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
// Copyright 2020 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 provides access to internal go/types APIs that are not
|
|
||||||
// yet exported.
|
|
||||||
package typesinternal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/token"
|
|
||||||
"go/types"
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SetUsesCgo(conf *types.Config) bool {
|
|
||||||
v := reflect.ValueOf(conf).Elem()
|
|
||||||
|
|
||||||
f := v.FieldByName("go115UsesCgo")
|
|
||||||
if !f.IsValid() {
|
|
||||||
f = v.FieldByName("UsesCgo")
|
|
||||||
if !f.IsValid() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := unsafe.Pointer(f.UnsafeAddr())
|
|
||||||
*(*bool)(addr) = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadGo116ErrorData extracts additional information from types.Error values
|
|
||||||
// generated by Go version 1.16 and later: the error code, start position, and
|
|
||||||
// end position. If all positions are valid, start <= err.Pos <= end.
|
|
||||||
//
|
|
||||||
// If the data could not be read, the final result parameter will be false.
|
|
||||||
func ReadGo116ErrorData(err types.Error) (code ErrorCode, start, end token.Pos, ok bool) {
|
|
||||||
var data [3]int
|
|
||||||
// By coincidence all of these fields are ints, which simplifies things.
|
|
||||||
v := reflect.ValueOf(err)
|
|
||||||
for i, name := range []string{"go116code", "go116start", "go116end"} {
|
|
||||||
f := v.FieldByName(name)
|
|
||||||
if !f.IsValid() {
|
|
||||||
return 0, 0, 0, false
|
|
||||||
}
|
|
||||||
data[i] = int(f.Int())
|
|
||||||
}
|
|
||||||
return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true
|
|
||||||
}
|
|
||||||
|
|
||||||
var SetGoVersion = func(conf *types.Config, version string) bool { return false }
|
|
|
@ -1,19 +0,0 @@
|
||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
//go:build go1.18
|
|
||||||
// +build go1.18
|
|
||||||
|
|
||||||
package typesinternal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SetGoVersion = func(conf *types.Config, version string) bool {
|
|
||||||
conf.GoVersion = version
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// This is a fork of internal/gover for use by x/tools until
|
|
||||||
// go1.21 and earlier are no longer supported by x/tools.
|
|
||||||
|
|
||||||
package versions
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]]
|
|
||||||
// The numbers are the original decimal strings to avoid integer overflows
|
|
||||||
// and since there is very little actual math. (Probably overflow doesn't matter in practice,
|
|
||||||
// but at the time this code was written, there was an existing test that used
|
|
||||||
// go1.99999999999, which does not fit in an int on 32-bit platforms.
|
|
||||||
// The "big decimal" representation avoids the problem entirely.)
|
|
||||||
type gover struct {
|
|
||||||
major string // decimal
|
|
||||||
minor string // decimal or ""
|
|
||||||
patch string // decimal or ""
|
|
||||||
kind string // "", "alpha", "beta", "rc"
|
|
||||||
pre string // decimal or ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// compare returns -1, 0, or +1 depending on whether
|
|
||||||
// x < y, x == y, or x > y, interpreted as toolchain versions.
|
|
||||||
// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
|
|
||||||
// Malformed versions compare less than well-formed versions and equal to each other.
|
|
||||||
// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
|
|
||||||
func compare(x, y string) int {
|
|
||||||
vx := parse(x)
|
|
||||||
vy := parse(y)
|
|
||||||
|
|
||||||
if c := cmpInt(vx.major, vy.major); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if c := cmpInt(vx.minor, vy.minor); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if c := cmpInt(vx.patch, vy.patch); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
if c := cmpInt(vx.pre, vy.pre); c != 0 {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// lang returns the Go language version. For example, lang("1.2.3") == "1.2".
|
|
||||||
func lang(x string) string {
|
|
||||||
v := parse(x)
|
|
||||||
if v.minor == "" || v.major == "1" && v.minor == "0" {
|
|
||||||
return v.major
|
|
||||||
}
|
|
||||||
return v.major + "." + v.minor
|
|
||||||
}
|
|
||||||
|
|
||||||
// isValid reports whether the version x is valid.
|
|
||||||
func isValid(x string) bool {
|
|
||||||
return parse(x) != gover{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse parses the Go version string x into a version.
|
|
||||||
// It returns the zero version if x is malformed.
|
|
||||||
func parse(x string) gover {
|
|
||||||
var v gover
|
|
||||||
|
|
||||||
// Parse major version.
|
|
||||||
var ok bool
|
|
||||||
v.major, x, ok = cutInt(x)
|
|
||||||
if !ok {
|
|
||||||
return gover{}
|
|
||||||
}
|
|
||||||
if x == "" {
|
|
||||||
// Interpret "1" as "1.0.0".
|
|
||||||
v.minor = "0"
|
|
||||||
v.patch = "0"
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse . before minor version.
|
|
||||||
if x[0] != '.' {
|
|
||||||
return gover{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse minor version.
|
|
||||||
v.minor, x, ok = cutInt(x[1:])
|
|
||||||
if !ok {
|
|
||||||
return gover{}
|
|
||||||
}
|
|
||||||
if x == "" {
|
|
||||||
// Patch missing is same as "0" for older versions.
|
|
||||||
// Starting in Go 1.21, patch missing is different from explicit .0.
|
|
||||||
if cmpInt(v.minor, "21") < 0 {
|
|
||||||
v.patch = "0"
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse patch if present.
|
|
||||||
if x[0] == '.' {
|
|
||||||
v.patch, x, ok = cutInt(x[1:])
|
|
||||||
if !ok || x != "" {
|
|
||||||
// Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
|
|
||||||
// Allowing them would be a bit confusing because we already have:
|
|
||||||
// 1.21 < 1.21rc1
|
|
||||||
// But a prerelease of a patch would have the opposite effect:
|
|
||||||
// 1.21.3rc1 < 1.21.3
|
|
||||||
// We've never needed them before, so let's not start now.
|
|
||||||
return gover{}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse prerelease.
|
|
||||||
i := 0
|
|
||||||
for i < len(x) && (x[i] < '0' || '9' < x[i]) {
|
|
||||||
if x[i] < 'a' || 'z' < x[i] {
|
|
||||||
return gover{}
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
return gover{}
|
|
||||||
}
|
|
||||||
v.kind, x = x[:i], x[i:]
|
|
||||||
if x == "" {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
v.pre, x, ok = cutInt(x)
|
|
||||||
if !ok || x != "" {
|
|
||||||
return gover{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// cutInt scans the leading decimal number at the start of x to an integer
|
|
||||||
// and returns that value and the rest of the string.
|
|
||||||
func cutInt(x string) (n, rest string, ok bool) {
|
|
||||||
i := 0
|
|
||||||
for i < len(x) && '0' <= x[i] && x[i] <= '9' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero
|
|
||||||
return "", "", false
|
|
||||||
}
|
|
||||||
return x[:i], x[i:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
|
|
||||||
// (Copied from golang.org/x/mod/semver's compareInt.)
|
|
||||||
func cmpInt(x, y string) int {
|
|
||||||
if x == y {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if len(x) < len(y) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if len(x) > len(y) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
if x < y {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// 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 versions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GoVersion returns the Go version of the type package.
|
|
||||||
// It returns zero if no version can be determined.
|
|
||||||
func GoVersion(pkg *types.Package) string {
|
|
||||||
// TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25.
|
|
||||||
if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok {
|
|
||||||
return pkg.GoVersion()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
//go:build !go1.22
|
|
||||||
// +build !go1.22
|
|
||||||
|
|
||||||
package versions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileVersions always reports the a file's Go version as the
|
|
||||||
// zero version at this Go version.
|
|
||||||
func FileVersions(info *types.Info, file *ast.File) string { return "" }
|
|
||||||
|
|
||||||
// InitFileVersions is a noop at this Go version.
|
|
||||||
func InitFileVersions(*types.Info) {}
|
|
|
@ -1,24 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
//go:build go1.22
|
|
||||||
// +build go1.22
|
|
||||||
|
|
||||||
package versions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
"go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileVersions maps a file to the file's semantic Go version.
|
|
||||||
// The reported version is the zero version if a version cannot be determined.
|
|
||||||
func FileVersions(info *types.Info, file *ast.File) string {
|
|
||||||
return info.FileVersions[file]
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitFileVersions initializes info to record Go versions for Go files.
|
|
||||||
func InitFileVersions(info *types.Info) {
|
|
||||||
info.FileVersions = make(map[*ast.File]string)
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
//go:build !go1.22
|
|
||||||
// +build !go1.22
|
|
||||||
|
|
||||||
package versions
|
|
||||||
|
|
||||||
// Lang returns the Go language version for version x.
|
|
||||||
// If x is not a valid version, Lang returns the empty string.
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// Lang("go1.21rc2") = "go1.21"
|
|
||||||
// Lang("go1.21.2") = "go1.21"
|
|
||||||
// Lang("go1.21") = "go1.21"
|
|
||||||
// Lang("go1") = "go1"
|
|
||||||
// Lang("bad") = ""
|
|
||||||
// Lang("1.21") = ""
|
|
||||||
func Lang(x string) string {
|
|
||||||
v := lang(stripGo(x))
|
|
||||||
if v == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return x[:2+len(v)] // "go"+v without allocation
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare returns -1, 0, or +1 depending on whether
|
|
||||||
// x < y, x == y, or x > y, interpreted as Go versions.
|
|
||||||
// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21".
|
|
||||||
// Invalid versions, including the empty string, compare less than
|
|
||||||
// valid versions and equal to each other.
|
|
||||||
// The language version "go1.21" compares less than the
|
|
||||||
// release candidate and eventual releases "go1.21rc1" and "go1.21.0".
|
|
||||||
// Custom toolchain suffixes are ignored during comparison:
|
|
||||||
// "go1.21.0" and "go1.21.0-bigcorp" are equal.
|
|
||||||
func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) }
|
|
||||||
|
|
||||||
// IsValid reports whether the version x is valid.
|
|
||||||
func IsValid(x string) bool { return isValid(stripGo(x)) }
|
|
||||||
|
|
||||||
// stripGo converts from a "go1.21" version to a "1.21" version.
|
|
||||||
// If v does not start with "go", stripGo returns the empty string (a known invalid version).
|
|
||||||
func stripGo(v string) string {
|
|
||||||
if len(v) < 2 || v[:2] != "go" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return v[2:]
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
//go:build go1.22
|
|
||||||
// +build go1.22
|
|
||||||
|
|
||||||
package versions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Lang returns the Go language version for version x.
|
|
||||||
// If x is not a valid version, Lang returns the empty string.
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// Lang("go1.21rc2") = "go1.21"
|
|
||||||
// Lang("go1.21.2") = "go1.21"
|
|
||||||
// Lang("go1.21") = "go1.21"
|
|
||||||
// Lang("go1") = "go1"
|
|
||||||
// Lang("bad") = ""
|
|
||||||
// Lang("1.21") = ""
|
|
||||||
func Lang(x string) string { return version.Lang(x) }
|
|
||||||
|
|
||||||
// Compare returns -1, 0, or +1 depending on whether
|
|
||||||
// x < y, x == y, or x > y, interpreted as Go versions.
|
|
||||||
// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21".
|
|
||||||
// Invalid versions, including the empty string, compare less than
|
|
||||||
// valid versions and equal to each other.
|
|
||||||
// The language version "go1.21" compares less than the
|
|
||||||
// release candidate and eventual releases "go1.21rc1" and "go1.21.0".
|
|
||||||
// Custom toolchain suffixes are ignored during comparison:
|
|
||||||
// "go1.21.0" and "go1.21.0-bigcorp" are equal.
|
|
||||||
func Compare(x, y string) int { return version.Compare(x, y) }
|
|
||||||
|
|
||||||
// IsValid reports whether the version x is valid.
|
|
||||||
func IsValid(x string) bool { return version.IsValid(x) }
|
|
|
@ -5,8 +5,8 @@ dario.cat/mergo
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/Azure/go-ansiterm
|
github.com/Azure/go-ansiterm
|
||||||
github.com/Azure/go-ansiterm/winterm
|
github.com/Azure/go-ansiterm/winterm
|
||||||
# github.com/Microsoft/go-winio v0.6.1
|
# github.com/Microsoft/go-winio v0.6.2
|
||||||
## explicit; go 1.17
|
## explicit; go 1.21
|
||||||
github.com/Microsoft/go-winio
|
github.com/Microsoft/go-winio
|
||||||
github.com/Microsoft/go-winio/internal/fs
|
github.com/Microsoft/go-winio/internal/fs
|
||||||
github.com/Microsoft/go-winio/internal/socket
|
github.com/Microsoft/go-winio/internal/socket
|
||||||
|
@ -364,9 +364,6 @@ go.opentelemetry.io/proto/otlp/trace/v1
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
golang.org/x/crypto/ed25519
|
golang.org/x/crypto/ed25519
|
||||||
golang.org/x/crypto/pbkdf2
|
golang.org/x/crypto/pbkdf2
|
||||||
# golang.org/x/mod v0.14.0
|
|
||||||
## explicit; go 1.18
|
|
||||||
golang.org/x/mod/semver
|
|
||||||
# golang.org/x/net v0.23.0
|
# golang.org/x/net v0.23.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
golang.org/x/net/http/httpguts
|
golang.org/x/net/http/httpguts
|
||||||
|
@ -397,26 +394,6 @@ golang.org/x/text/width
|
||||||
# golang.org/x/time v0.3.0
|
# golang.org/x/time v0.3.0
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/time/rate
|
golang.org/x/time/rate
|
||||||
# golang.org/x/tools v0.16.0
|
|
||||||
## explicit; go 1.18
|
|
||||||
golang.org/x/tools/cmd/stringer
|
|
||||||
golang.org/x/tools/go/gcexportdata
|
|
||||||
golang.org/x/tools/go/internal/packagesdriver
|
|
||||||
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/core
|
|
||||||
golang.org/x/tools/internal/event/keys
|
|
||||||
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/gocommand
|
|
||||||
golang.org/x/tools/internal/packagesinternal
|
|
||||||
golang.org/x/tools/internal/pkgbits
|
|
||||||
golang.org/x/tools/internal/tokeninternal
|
|
||||||
golang.org/x/tools/internal/typeparams
|
|
||||||
golang.org/x/tools/internal/typesinternal
|
|
||||||
golang.org/x/tools/internal/versions
|
|
||||||
# google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97
|
# google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
google.golang.org/genproto/googleapis/api/httpbody
|
google.golang.org/genproto/googleapis/api/httpbody
|
||||||
|
|
Loading…
Reference in New Issue