mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-13 10:20:55 +08:00
libgo: update to final Go 1.8 release
Along with the update this fixes a problem that was always present but only showed up with the new reflect test. When a program used a **unsafe.Pointer and stored the value in an interface type, the generated type descriptor pointed to the GC data for *unsafe.Pointer. It did that by name, but we were not generating a variable with the right name. Reviewed-on: https://go-review.googlesource.com/37144 From-SVN: r245535
This commit is contained in:
parent
4bcd6597a3
commit
00b2a30fd4
@ -1,4 +1,4 @@
|
||||
c3935e1f20ad5b1d4c41150f11fb266913c04df7
|
||||
893f0e4a707c6f10eb14842b18954486042f0fb3
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
@ -1,4 +1,4 @@
|
||||
2a5f65a98ca483aad2dd74dc2636a7baecc59cf2
|
||||
cd6b6202dd1559b3ac63179b45f1833fcfbe7eca
|
||||
|
||||
The first line of this file holds the git revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
@ -1 +1 @@
|
||||
go1.8rc3
|
||||
go1.8
|
||||
|
@ -17,7 +17,7 @@
|
||||
// clean remove object files
|
||||
// doc show documentation for package or symbol
|
||||
// env print Go environment information
|
||||
// bug print information for bug reports
|
||||
// bug start a bug report
|
||||
// fix run go tool fix on packages
|
||||
// fmt run gofmt on package sources
|
||||
// generate generate Go files by processing source
|
||||
@ -324,15 +324,14 @@
|
||||
// each named variable on its own line.
|
||||
//
|
||||
//
|
||||
// Print information for bug reports
|
||||
// Start a bug report
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go bug
|
||||
//
|
||||
// Bug prints information that helps file effective bug reports.
|
||||
//
|
||||
// Bugs may be reported at https://golang.org/issue/new.
|
||||
// Bug opens the default browser and starts a new bug report.
|
||||
// The report includes useful system information.
|
||||
//
|
||||
//
|
||||
// Run go tool fix on packages
|
||||
|
@ -428,7 +428,7 @@ func downloadPackage(p *Package) error {
|
||||
return fmt.Errorf("cannot download, $GOPATH not set. For more details see: 'go help gopath'")
|
||||
}
|
||||
// Guard against people setting GOPATH=$GOROOT.
|
||||
if list[0] == goroot {
|
||||
if filepath.Clean(list[0]) == filepath.Clean(goroot) {
|
||||
return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil {
|
||||
|
@ -1683,173 +1683,111 @@ func homeEnvName() string {
|
||||
}
|
||||
}
|
||||
|
||||
// Test go env missing GOPATH shows default.
|
||||
func TestMissingGOPATHEnvShowsDefault(t *testing.T) {
|
||||
func TestDefaultGOPATH(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.setenv("GOPATH", "")
|
||||
tg.tempDir("home/go")
|
||||
tg.setenv(homeEnvName(), tg.path("home"))
|
||||
|
||||
tg.run("env", "GOPATH")
|
||||
tg.grepStdout(regexp.QuoteMeta(tg.path("home/go")), "want GOPATH=$HOME/go")
|
||||
|
||||
want := filepath.Join(os.Getenv(homeEnvName()), "go")
|
||||
got := strings.TrimSpace(tg.getStdout())
|
||||
if got != want {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
tg.setenv("GOROOT", tg.path("home/go"))
|
||||
tg.run("env", "GOPATH")
|
||||
tg.grepStdoutNot(".", "want unset GOPATH because GOROOT=$HOME/go")
|
||||
|
||||
tg.setenv("GOROOT", tg.path("home/go")+"/")
|
||||
tg.run("env", "GOPATH")
|
||||
tg.grepStdoutNot(".", "want unset GOPATH because GOROOT=$HOME/go/")
|
||||
}
|
||||
|
||||
// Test go get missing GOPATH causes go get to warn if directory doesn't exist.
|
||||
func TestMissingGOPATHGetWarnsIfNotExists(t *testing.T) {
|
||||
func TestDefaultGOPATHGet(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
t.Skip("skipping because git binary not found")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
||||
// setenv variables for test and defer deleting temporary home directory.
|
||||
tg.setenv("GOPATH", "")
|
||||
tmp, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create tmp home: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
tg.setenv(homeEnvName(), tmp)
|
||||
tg.tempDir("home")
|
||||
tg.setenv(homeEnvName(), tg.path("home"))
|
||||
|
||||
// warn for creating directory
|
||||
tg.run("get", "-v", "github.com/golang/example/hello")
|
||||
tg.grepStderr("created GOPATH="+regexp.QuoteMeta(tg.path("home/go"))+"; see 'go help gopath'", "did not create GOPATH")
|
||||
|
||||
want := fmt.Sprintf("created GOPATH=%s; see 'go help gopath'", filepath.Join(tmp, "go"))
|
||||
got := strings.TrimSpace(tg.getStderr())
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Test go get missing GOPATH causes no warning if directory exists.
|
||||
func TestMissingGOPATHGetDoesntWarnIfExists(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
t.Skip("skipping because git binary not found")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
||||
// setenv variables for test and defer resetting them.
|
||||
tg.setenv("GOPATH", "")
|
||||
tmp, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create tmp home: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
|
||||
t.Fatalf("could not create $HOME/go: %v", err)
|
||||
}
|
||||
|
||||
tg.setenv(homeEnvName(), tmp)
|
||||
|
||||
// no warning if directory already exists
|
||||
tg.must(os.RemoveAll(tg.path("home/go")))
|
||||
tg.tempDir("home/go")
|
||||
tg.run("get", "github.com/golang/example/hello")
|
||||
tg.grepStderrNot(".", "expected no output on standard error")
|
||||
|
||||
got := strings.TrimSpace(tg.getStderr())
|
||||
if got != "" {
|
||||
t.Errorf("got %q; wants empty", got)
|
||||
}
|
||||
// error if $HOME/go is a file
|
||||
tg.must(os.RemoveAll(tg.path("home/go")))
|
||||
tg.tempFile("home/go", "")
|
||||
tg.runFail("get", "github.com/golang/example/hello")
|
||||
tg.grepStderr(`mkdir .*[/\\]go: .*(not a directory|cannot find the path)`, "expected error because $HOME/go is a file")
|
||||
}
|
||||
|
||||
// Test go get missing GOPATH fails if pointed file is not a directory.
|
||||
func TestMissingGOPATHGetFailsIfItsNotDirectory(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
func TestDefaultGOPATHPrintedSearchList(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
||||
// setenv variables for test and defer resetting them.
|
||||
tg.setenv("GOPATH", "")
|
||||
tmp, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create tmp home: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
tg.tempDir("home")
|
||||
tg.setenv(homeEnvName(), tg.path("home"))
|
||||
|
||||
path := filepath.Join(tmp, "go")
|
||||
if err := ioutil.WriteFile(path, nil, 0777); err != nil {
|
||||
t.Fatalf("could not create GOPATH at %s: %v", path, err)
|
||||
}
|
||||
tg.setenv(homeEnvName(), tmp)
|
||||
|
||||
const pkg = "github.com/golang/example/hello"
|
||||
tg.runFail("get", pkg)
|
||||
|
||||
msg := "not a directory"
|
||||
if runtime.GOOS == "windows" {
|
||||
msg = "The system cannot find the path specified."
|
||||
}
|
||||
want := fmt.Sprintf("package %s: mkdir %s: %s", pkg, filepath.Join(tmp, "go"), msg)
|
||||
got := strings.TrimSpace(tg.getStderr())
|
||||
if got != want {
|
||||
t.Errorf("got %q; wants %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Test go install of missing package when missing GOPATH fails and shows default GOPATH.
|
||||
func TestMissingGOPATHInstallMissingPackageFailsAndShowsDefault(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
|
||||
// setenv variables for test and defer resetting them.
|
||||
tg.setenv("GOPATH", "")
|
||||
tmp, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create tmp home: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
|
||||
t.Fatalf("could not create $HOME/go: %v", err)
|
||||
}
|
||||
tg.setenv(homeEnvName(), tmp)
|
||||
|
||||
const pkg = "github.com/golang/example/hello"
|
||||
tg.runFail("install", pkg)
|
||||
|
||||
pkgPath := filepath.Join(strings.Split(pkg, "/")...)
|
||||
want := fmt.Sprintf("can't load package: package %s: cannot find package \"%s\" in any of:", pkg, pkg) +
|
||||
fmt.Sprintf("\n\t%s (from $GOROOT)", filepath.Join(runtime.GOROOT(), "src", pkgPath)) +
|
||||
fmt.Sprintf("\n\t%s (from $GOPATH)", filepath.Join(tmp, "go", "src", pkgPath))
|
||||
|
||||
got := strings.TrimSpace(tg.getStderr())
|
||||
if got != want {
|
||||
t.Errorf("got %q; wants %q", got, want)
|
||||
}
|
||||
tg.runFail("install", "github.com/golang/example/hello")
|
||||
tg.grepStderr(regexp.QuoteMeta(tg.path("home/go/src/github.com/golang/example/hello"))+`.*from \$GOPATH`, "expected default GOPATH")
|
||||
}
|
||||
|
||||
// Issue 4186. go get cannot be used to download packages to $GOROOT.
|
||||
// Test that without GOPATH set, go get should fail.
|
||||
func TestWithoutGOPATHGoGetFails(t *testing.T) {
|
||||
func TestGoGetIntoGOROOT(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempDir("src")
|
||||
tg.setenv("GOPATH", "")
|
||||
tg.setenv("GOROOT", tg.path("."))
|
||||
tg.runFail("get", "-d", "golang.org/x/codereview/cmd/hgpatch")
|
||||
}
|
||||
|
||||
// Test that with GOPATH=$GOROOT, go get should fail.
|
||||
func TestWithGOPATHEqualsGOROOTGoGetFails(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.parallel()
|
||||
tg.tempDir("src")
|
||||
// Fails because GOROOT=GOPATH
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.setenv("GOROOT", tg.path("."))
|
||||
tg.runFail("get", "-d", "golang.org/x/codereview/cmd/hgpatch")
|
||||
tg.runFail("get", "-d", "github.com/golang/example/hello")
|
||||
tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
|
||||
tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
|
||||
|
||||
// Fails because GOROOT=GOPATH after cleaning.
|
||||
tg.setenv("GOPATH", tg.path(".")+"/")
|
||||
tg.setenv("GOROOT", tg.path("."))
|
||||
tg.runFail("get", "-d", "github.com/golang/example/hello")
|
||||
tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
|
||||
tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
|
||||
|
||||
tg.setenv("GOPATH", tg.path("."))
|
||||
tg.setenv("GOROOT", tg.path(".")+"/")
|
||||
tg.runFail("get", "-d", "github.com/golang/example/hello")
|
||||
tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
|
||||
tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
|
||||
|
||||
// Fails because GOROOT=$HOME/go so default GOPATH unset.
|
||||
tg.tempDir("home/go")
|
||||
tg.setenv(homeEnvName(), tg.path("home"))
|
||||
tg.setenv("GOPATH", "")
|
||||
tg.setenv("GOROOT", tg.path("home/go"))
|
||||
tg.runFail("get", "-d", "github.com/golang/example/hello")
|
||||
tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
|
||||
|
||||
tg.setenv(homeEnvName(), tg.path("home")+"/")
|
||||
tg.setenv("GOPATH", "")
|
||||
tg.setenv("GOROOT", tg.path("home/go"))
|
||||
tg.runFail("get", "-d", "github.com/golang/example/hello")
|
||||
tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
|
||||
|
||||
tg.setenv(homeEnvName(), tg.path("home"))
|
||||
tg.setenv("GOPATH", "")
|
||||
tg.setenv("GOROOT", tg.path("home/go")+"/")
|
||||
tg.runFail("get", "-d", "github.com/golang/example/hello")
|
||||
tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
|
||||
}
|
||||
|
||||
func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) {
|
||||
@ -3744,6 +3682,13 @@ func TestMatchesOnlySubtestParallelIsOK(t *testing.T) {
|
||||
tg.grepBoth(okPattern, "go test did not say ok")
|
||||
}
|
||||
|
||||
// Issue 18845
|
||||
func TestBenchTimeout(t *testing.T) {
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.run("test", "-bench", ".", "-timeout", "750ms", "testdata/timeoutbench_test.go")
|
||||
}
|
||||
|
||||
func TestLinkXImportPathEscape(t *testing.T) {
|
||||
// golang.org/issue/16710
|
||||
tg := testgo(t)
|
||||
|
@ -136,7 +136,7 @@ func main() {
|
||||
// Diagnose common mistake: GOPATH==GOROOT.
|
||||
// This setting is equivalent to not setting GOPATH at all,
|
||||
// which is not what most people want when they do it.
|
||||
if gopath := buildContext.GOPATH; gopath == runtime.GOROOT() {
|
||||
if gopath := buildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(runtime.GOROOT()) {
|
||||
fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
|
||||
} else {
|
||||
for _, p := range filepath.SplitList(gopath) {
|
||||
|
@ -7,8 +7,8 @@ package x509
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
|
||||
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
||||
"/etc/ssl/ca-bundle.pem", // OpenSUSE
|
||||
"/etc/pki/tls/cacert.pem", // OpenELEC
|
||||
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
|
||||
}
|
||||
|
@ -35,15 +35,12 @@ func ctxDriverExec(ctx context.Context, execer driver.Execer, query string, nvda
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resi, err := execer.Exec(query, dargs)
|
||||
if err == nil {
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return resi, ctx.Err()
|
||||
}
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
return resi, err
|
||||
return execer.Exec(query, dargs)
|
||||
}
|
||||
|
||||
func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, nvdargs []driver.NamedValue) (driver.Rows, error) {
|
||||
@ -56,16 +53,12 @@ func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, n
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rowsi, err := queryer.Query(query, dargs)
|
||||
if err == nil {
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
rowsi.Close()
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
return rowsi, err
|
||||
return queryer.Query(query, dargs)
|
||||
}
|
||||
|
||||
func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Result, error) {
|
||||
@ -77,15 +70,12 @@ func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, nvdargs []driver.Nam
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resi, err := si.Exec(dargs)
|
||||
if err == nil {
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return resi, ctx.Err()
|
||||
}
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
return resi, err
|
||||
return si.Exec(dargs)
|
||||
}
|
||||
|
||||
func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Rows, error) {
|
||||
@ -97,16 +87,12 @@ func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, nvdargs []driver.Na
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rowsi, err := si.Query(dargs)
|
||||
if err == nil {
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
rowsi.Close()
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
return rowsi, err
|
||||
return si.Query(dargs)
|
||||
}
|
||||
|
||||
var errLevelNotSupported = errors.New("sql: selected isolation level is not supported")
|
||||
|
@ -305,8 +305,9 @@ type DB struct {
|
||||
|
||||
mu sync.Mutex // protects following fields
|
||||
freeConn []*driverConn
|
||||
connRequests []chan connRequest
|
||||
numOpen int // number of opened and pending open connections
|
||||
connRequests map[uint64]chan connRequest
|
||||
nextRequest uint64 // Next key to use in connRequests.
|
||||
numOpen int // number of opened and pending open connections
|
||||
// Used to signal the need for new connections
|
||||
// a goroutine running connectionOpener() reads on this chan and
|
||||
// maybeOpenNewConnections sends on the chan (one send per needed connection)
|
||||
@ -572,10 +573,11 @@ func Open(driverName, dataSourceName string) (*DB, error) {
|
||||
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
|
||||
}
|
||||
db := &DB{
|
||||
driver: driveri,
|
||||
dsn: dataSourceName,
|
||||
openerCh: make(chan struct{}, connectionRequestQueueSize),
|
||||
lastPut: make(map[*driverConn]string),
|
||||
driver: driveri,
|
||||
dsn: dataSourceName,
|
||||
openerCh: make(chan struct{}, connectionRequestQueueSize),
|
||||
lastPut: make(map[*driverConn]string),
|
||||
connRequests: make(map[uint64]chan connRequest),
|
||||
}
|
||||
go db.connectionOpener()
|
||||
return db, nil
|
||||
@ -881,6 +883,14 @@ type connRequest struct {
|
||||
|
||||
var errDBClosed = errors.New("sql: database is closed")
|
||||
|
||||
// nextRequestKeyLocked returns the next connection request key.
|
||||
// It is assumed that nextRequest will not overflow.
|
||||
func (db *DB) nextRequestKeyLocked() uint64 {
|
||||
next := db.nextRequest
|
||||
db.nextRequest++
|
||||
return next
|
||||
}
|
||||
|
||||
// conn returns a newly-opened or cached *driverConn.
|
||||
func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn, error) {
|
||||
db.mu.Lock()
|
||||
@ -918,12 +928,25 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
|
||||
// Make the connRequest channel. It's buffered so that the
|
||||
// connectionOpener doesn't block while waiting for the req to be read.
|
||||
req := make(chan connRequest, 1)
|
||||
db.connRequests = append(db.connRequests, req)
|
||||
reqKey := db.nextRequestKeyLocked()
|
||||
db.connRequests[reqKey] = req
|
||||
db.mu.Unlock()
|
||||
|
||||
// Timeout the connection request with the context.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Remove the connection request and ensure no value has been sent
|
||||
// on it after removing.
|
||||
db.mu.Lock()
|
||||
delete(db.connRequests, reqKey)
|
||||
db.mu.Unlock()
|
||||
select {
|
||||
default:
|
||||
case ret, ok := <-req:
|
||||
if ok {
|
||||
db.putConn(ret.conn, ret.err)
|
||||
}
|
||||
}
|
||||
return nil, ctx.Err()
|
||||
case ret, ok := <-req:
|
||||
if !ok {
|
||||
@ -1044,12 +1067,12 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
|
||||
return false
|
||||
}
|
||||
if c := len(db.connRequests); c > 0 {
|
||||
req := db.connRequests[0]
|
||||
// This copy is O(n) but in practice faster than a linked list.
|
||||
// TODO: consider compacting it down less often and
|
||||
// moving the base instead?
|
||||
copy(db.connRequests, db.connRequests[1:])
|
||||
db.connRequests = db.connRequests[:c-1]
|
||||
var req chan connRequest
|
||||
var reqKey uint64
|
||||
for reqKey, req = range db.connRequests {
|
||||
break
|
||||
}
|
||||
delete(db.connRequests, reqKey) // Remove from pending requests.
|
||||
if err == nil {
|
||||
dc.inUse = true
|
||||
}
|
||||
@ -2071,14 +2094,21 @@ type Rows struct {
|
||||
dc *driverConn // owned; must call releaseConn when closed to release
|
||||
releaseConn func(error)
|
||||
rowsi driver.Rows
|
||||
cancel func() // called when Rows is closed, may be nil.
|
||||
closeStmt *driverStmt // if non-nil, statement to Close on close
|
||||
|
||||
// closed value is 1 when the Rows is closed.
|
||||
// Use atomic operations on value when checking value.
|
||||
closed int32
|
||||
cancel func() // called when Rows is closed, may be nil.
|
||||
lastcols []driver.Value
|
||||
lasterr error // non-nil only if closed is true
|
||||
closeStmt *driverStmt // if non-nil, statement to Close on close
|
||||
// closemu prevents Rows from closing while there
|
||||
// is an active streaming result. It is held for read during non-close operations
|
||||
// and exclusively during close.
|
||||
//
|
||||
// closemu guards lasterr and closed.
|
||||
closemu sync.RWMutex
|
||||
closed bool
|
||||
lasterr error // non-nil only if closed is true
|
||||
|
||||
// lastcols is only used in Scan, Next, and NextResultSet which are expected
|
||||
// not not be called concurrently.
|
||||
lastcols []driver.Value
|
||||
}
|
||||
|
||||
func (rs *Rows) initContextClose(ctx context.Context) {
|
||||
@ -2089,7 +2119,7 @@ func (rs *Rows) initContextClose(ctx context.Context) {
|
||||
// awaitDone blocks until the rows are closed or the context canceled.
|
||||
func (rs *Rows) awaitDone(ctx context.Context) {
|
||||
<-ctx.Done()
|
||||
rs.Close()
|
||||
rs.close(ctx.Err())
|
||||
}
|
||||
|
||||
// Next prepares the next result row for reading with the Scan method. It
|
||||
@ -2099,8 +2129,19 @@ func (rs *Rows) awaitDone(ctx context.Context) {
|
||||
//
|
||||
// Every call to Scan, even the first one, must be preceded by a call to Next.
|
||||
func (rs *Rows) Next() bool {
|
||||
if rs.isClosed() {
|
||||
return false
|
||||
var doClose, ok bool
|
||||
withLock(rs.closemu.RLocker(), func() {
|
||||
doClose, ok = rs.nextLocked()
|
||||
})
|
||||
if doClose {
|
||||
rs.Close()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func (rs *Rows) nextLocked() (doClose, ok bool) {
|
||||
if rs.closed {
|
||||
return false, false
|
||||
}
|
||||
if rs.lastcols == nil {
|
||||
rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns()))
|
||||
@ -2109,23 +2150,21 @@ func (rs *Rows) Next() bool {
|
||||
if rs.lasterr != nil {
|
||||
// Close the connection if there is a driver error.
|
||||
if rs.lasterr != io.EOF {
|
||||
rs.Close()
|
||||
return false
|
||||
return true, false
|
||||
}
|
||||
nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
|
||||
if !ok {
|
||||
rs.Close()
|
||||
return false
|
||||
return true, false
|
||||
}
|
||||
// The driver is at the end of the current result set.
|
||||
// Test to see if there is another result set after the current one.
|
||||
// Only close Rows if there is no further result sets to read.
|
||||
if !nextResultSet.HasNextResultSet() {
|
||||
rs.Close()
|
||||
doClose = true
|
||||
}
|
||||
return false
|
||||
return doClose, false
|
||||
}
|
||||
return true
|
||||
return false, true
|
||||
}
|
||||
|
||||
// NextResultSet prepares the next result set for reading. It returns true if
|
||||
@ -2137,18 +2176,28 @@ func (rs *Rows) Next() bool {
|
||||
// scanning. If there are further result sets they may not have rows in the result
|
||||
// set.
|
||||
func (rs *Rows) NextResultSet() bool {
|
||||
if rs.isClosed() {
|
||||
var doClose bool
|
||||
defer func() {
|
||||
if doClose {
|
||||
rs.Close()
|
||||
}
|
||||
}()
|
||||
rs.closemu.RLock()
|
||||
defer rs.closemu.RUnlock()
|
||||
|
||||
if rs.closed {
|
||||
return false
|
||||
}
|
||||
|
||||
rs.lastcols = nil
|
||||
nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
|
||||
if !ok {
|
||||
rs.Close()
|
||||
doClose = true
|
||||
return false
|
||||
}
|
||||
rs.lasterr = nextResultSet.NextResultSet()
|
||||
if rs.lasterr != nil {
|
||||
rs.Close()
|
||||
doClose = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -2157,6 +2206,8 @@ func (rs *Rows) NextResultSet() bool {
|
||||
// Err returns the error, if any, that was encountered during iteration.
|
||||
// Err may be called after an explicit or implicit Close.
|
||||
func (rs *Rows) Err() error {
|
||||
rs.closemu.RLock()
|
||||
defer rs.closemu.RUnlock()
|
||||
if rs.lasterr == io.EOF {
|
||||
return nil
|
||||
}
|
||||
@ -2167,7 +2218,9 @@ func (rs *Rows) Err() error {
|
||||
// Columns returns an error if the rows are closed, or if the rows
|
||||
// are from QueryRow and there was a deferred error.
|
||||
func (rs *Rows) Columns() ([]string, error) {
|
||||
if rs.isClosed() {
|
||||
rs.closemu.RLock()
|
||||
defer rs.closemu.RUnlock()
|
||||
if rs.closed {
|
||||
return nil, errors.New("sql: Rows are closed")
|
||||
}
|
||||
if rs.rowsi == nil {
|
||||
@ -2179,7 +2232,9 @@ func (rs *Rows) Columns() ([]string, error) {
|
||||
// ColumnTypes returns column information such as column type, length,
|
||||
// and nullable. Some information may not be available from some drivers.
|
||||
func (rs *Rows) ColumnTypes() ([]*ColumnType, error) {
|
||||
if rs.isClosed() {
|
||||
rs.closemu.RLock()
|
||||
defer rs.closemu.RUnlock()
|
||||
if rs.closed {
|
||||
return nil, errors.New("sql: Rows are closed")
|
||||
}
|
||||
if rs.rowsi == nil {
|
||||
@ -2329,9 +2384,13 @@ func rowsColumnInfoSetup(rowsi driver.Rows) []*ColumnType {
|
||||
// For scanning into *bool, the source may be true, false, 1, 0, or
|
||||
// string inputs parseable by strconv.ParseBool.
|
||||
func (rs *Rows) Scan(dest ...interface{}) error {
|
||||
if rs.isClosed() {
|
||||
rs.closemu.RLock()
|
||||
if rs.closed {
|
||||
rs.closemu.RUnlock()
|
||||
return errors.New("sql: Rows are closed")
|
||||
}
|
||||
rs.closemu.RUnlock()
|
||||
|
||||
if rs.lastcols == nil {
|
||||
return errors.New("sql: Scan called without calling Next")
|
||||
}
|
||||
@ -2351,20 +2410,28 @@ func (rs *Rows) Scan(dest ...interface{}) error {
|
||||
// hook throug a test only mutex.
|
||||
var rowsCloseHook = func() func(*Rows, *error) { return nil }
|
||||
|
||||
func (rs *Rows) isClosed() bool {
|
||||
return atomic.LoadInt32(&rs.closed) != 0
|
||||
}
|
||||
|
||||
// Close closes the Rows, preventing further enumeration. If Next is called
|
||||
// and returns false and there are no further result sets,
|
||||
// the Rows are closed automatically and it will suffice to check the
|
||||
// result of Err. Close is idempotent and does not affect the result of Err.
|
||||
func (rs *Rows) Close() error {
|
||||
if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) {
|
||||
return rs.close(nil)
|
||||
}
|
||||
|
||||
func (rs *Rows) close(err error) error {
|
||||
rs.closemu.Lock()
|
||||
defer rs.closemu.Unlock()
|
||||
|
||||
if rs.closed {
|
||||
return nil
|
||||
}
|
||||
rs.closed = true
|
||||
|
||||
err := rs.rowsi.Close()
|
||||
if rs.lasterr == nil {
|
||||
rs.lasterr = err
|
||||
}
|
||||
|
||||
err = rs.rowsi.Close()
|
||||
if fn := rowsCloseHook(); fn != nil {
|
||||
fn(rs, &err)
|
||||
}
|
||||
|
@ -153,8 +153,13 @@ func closeDB(t testing.TB, db *DB) {
|
||||
if err != nil {
|
||||
t.Fatalf("error closing DB: %v", err)
|
||||
}
|
||||
if count := db.numOpenConns(); count != 0 {
|
||||
t.Fatalf("%d connections still open after closing DB", count)
|
||||
|
||||
var numOpen int
|
||||
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
|
||||
numOpen = db.numOpenConns()
|
||||
return numOpen == 0
|
||||
}) {
|
||||
t.Fatalf("%d connections still open after closing DB", numOpen)
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,6 +281,7 @@ func TestQuery(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestQueryContext tests canceling the context while scanning the rows.
|
||||
func TestQueryContext(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
@ -297,7 +303,7 @@ func TestQueryContext(t *testing.T) {
|
||||
for rows.Next() {
|
||||
if index == 2 {
|
||||
cancel()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
waitForRowsClose(t, rows, 5*time.Second)
|
||||
}
|
||||
var r row
|
||||
err = rows.Scan(&r.age, &r.name)
|
||||
@ -313,9 +319,13 @@ func TestQueryContext(t *testing.T) {
|
||||
got = append(got, r)
|
||||
index++
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if err := ctx.Err(); err != context.Canceled {
|
||||
t.Fatalf("context err = %v; want context.Canceled")
|
||||
}
|
||||
default:
|
||||
t.Fatalf("context err = nil; want context.Canceled")
|
||||
}
|
||||
want := []row{
|
||||
{age: 1, name: "Alice"},
|
||||
@ -327,6 +337,7 @@ func TestQueryContext(t *testing.T) {
|
||||
|
||||
// And verify that the final rows.Next() call, which hit EOF,
|
||||
// also closed the rows connection.
|
||||
waitForRowsClose(t, rows, 5*time.Second)
|
||||
waitForFree(t, db, 5*time.Second, 1)
|
||||
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
|
||||
t.Errorf("executed %d Prepare statements; want 1", prepares)
|
||||
@ -356,12 +367,27 @@ func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
|
||||
}
|
||||
}
|
||||
|
||||
func waitForRowsClose(t *testing.T, rows *Rows, maxWait time.Duration) {
|
||||
if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
|
||||
rows.closemu.RLock()
|
||||
defer rows.closemu.RUnlock()
|
||||
return rows.closed
|
||||
}) {
|
||||
t.Fatal("failed to close rows")
|
||||
}
|
||||
}
|
||||
|
||||
// TestQueryContextWait ensures that rows and all internal statements are closed when
|
||||
// a query context is closed during execution.
|
||||
func TestQueryContextWait(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
prepares0 := numPrepares(t, db)
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*15)
|
||||
// TODO(kardianos): convert this from using a timeout to using an explicit
|
||||
// cancel when the query signals that is is "executing" the query.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
// This will trigger the *fakeConn.Prepare method which will take time
|
||||
// performing the query. The ctxDriverPrepare func will check the context
|
||||
@ -374,10 +400,15 @@ func TestQueryContextWait(t *testing.T) {
|
||||
// Verify closed rows connection after error condition.
|
||||
waitForFree(t, db, 5*time.Second, 1)
|
||||
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
|
||||
t.Errorf("executed %d Prepare statements; want 1", prepares)
|
||||
// TODO(kardianos): if the context timeouts before the db.QueryContext
|
||||
// executes this check may fail. After adjusting how the context
|
||||
// is canceled above revert this back to a Fatal error.
|
||||
t.Logf("executed %d Prepare statements; want 1", prepares)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTxContextWait tests the transaction behavior when the tx context is canceled
|
||||
// during execution of the query.
|
||||
func TestTxContextWait(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
@ -386,6 +417,10 @@ func TestTxContextWait(t *testing.T) {
|
||||
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
// Guard against the context being canceled before BeginTx completes.
|
||||
if err == context.DeadlineExceeded {
|
||||
t.Skip("tx context canceled prior to first use")
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -398,12 +433,6 @@ func TestTxContextWait(t *testing.T) {
|
||||
}
|
||||
|
||||
waitForFree(t, db, 5*time.Second, 0)
|
||||
|
||||
// Ensure the dropped connection allows more connections to be made.
|
||||
// Checked on DB Close.
|
||||
waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
|
||||
return db.numOpenConns() == 0
|
||||
})
|
||||
}
|
||||
|
||||
func TestMultiResultSetQuery(t *testing.T) {
|
||||
@ -527,6 +556,63 @@ func TestQueryNamedArg(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolExhaustOnCancel(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("long test")
|
||||
}
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
max := 3
|
||||
|
||||
db.SetMaxOpenConns(max)
|
||||
|
||||
// First saturate the connection pool.
|
||||
// Then start new requests for a connection that is cancelled after it is requested.
|
||||
|
||||
var saturate, saturateDone sync.WaitGroup
|
||||
saturate.Add(max)
|
||||
saturateDone.Add(max)
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
go func() {
|
||||
saturate.Done()
|
||||
rows, err := db.Query("WAIT|500ms|SELECT|people|name,photo|")
|
||||
if err != nil {
|
||||
t.Fatalf("Query: %v", err)
|
||||
}
|
||||
rows.Close()
|
||||
saturateDone.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
saturate.Wait()
|
||||
|
||||
// Now cancel the request while it is waiting.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||
defer cancel()
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
ctxReq, cancelReq := context.WithCancel(ctx)
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
cancelReq()
|
||||
}()
|
||||
err := db.PingContext(ctxReq)
|
||||
if err != context.Canceled {
|
||||
t.Fatalf("PingContext (Exhaust): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
saturateDone.Wait()
|
||||
|
||||
// Now try to open a normal connection.
|
||||
err := db.PingContext(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("PingContext (Normal): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteOwnership(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
@ -2677,7 +2763,6 @@ func TestIssue18429(t *testing.T) {
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
time.Sleep(milliWait * 3 * time.Millisecond)
|
||||
}
|
||||
|
||||
// TestIssue18719 closes the context right before use. The sql.driverConn
|
||||
@ -2720,14 +2805,8 @@ func TestIssue18719(t *testing.T) {
|
||||
// Do not explicitly rollback. The rollback will happen from the
|
||||
// canceled context.
|
||||
|
||||
// Wait for connections to return to pool.
|
||||
var numOpen int
|
||||
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
|
||||
numOpen = db.numOpenConns()
|
||||
return numOpen == 0
|
||||
}) {
|
||||
t.Fatalf("open conns after hitting EOF = %d; want 0", numOpen)
|
||||
}
|
||||
cancel()
|
||||
waitForRowsClose(t, rows, 5*time.Second)
|
||||
}
|
||||
|
||||
func TestConcurrency(t *testing.T) {
|
||||
|
@ -775,6 +775,20 @@ func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) (string, []
|
||||
|
||||
var ddBytes = []byte("--")
|
||||
|
||||
// indirect drills into interfaces and pointers, returning the pointed-at value.
|
||||
// If it encounters a nil interface or pointer, indirect returns that nil value.
|
||||
// This can turn into an infinite loop given a cyclic chain,
|
||||
// but it matches the Go 1 behavior.
|
||||
func indirect(vf reflect.Value) reflect.Value {
|
||||
for vf.Kind() == reflect.Interface || vf.Kind() == reflect.Ptr {
|
||||
if vf.IsNil() {
|
||||
return vf
|
||||
}
|
||||
vf = vf.Elem()
|
||||
}
|
||||
return vf
|
||||
}
|
||||
|
||||
func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||
s := parentStack{p: p}
|
||||
for i := range tinfo.fields {
|
||||
@ -816,17 +830,9 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Drill into interfaces and pointers.
|
||||
// This can turn into an infinite loop given a cyclic chain,
|
||||
// but it matches the Go 1 behavior.
|
||||
for vf.Kind() == reflect.Interface || vf.Kind() == reflect.Ptr {
|
||||
if vf.IsNil() {
|
||||
return nil
|
||||
}
|
||||
vf = vf.Elem()
|
||||
}
|
||||
|
||||
var scratch [64]byte
|
||||
vf = indirect(vf)
|
||||
switch vf.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if err := emit(p, strconv.AppendInt(scratch[:0], vf.Int(), 10)); err != nil {
|
||||
@ -861,6 +867,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||
if err := s.trim(finfo.parents); err != nil {
|
||||
return err
|
||||
}
|
||||
vf = indirect(vf)
|
||||
k := vf.Kind()
|
||||
if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) {
|
||||
return fmt.Errorf("xml: bad type for comment field of %s", val.Type())
|
||||
@ -901,6 +908,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
|
||||
continue
|
||||
|
||||
case fInnerXml:
|
||||
vf = indirect(vf)
|
||||
iface := vf.Interface()
|
||||
switch raw := iface.(type) {
|
||||
case []byte:
|
||||
|
@ -386,6 +386,140 @@ func ifaceptr(x interface{}) interface{} {
|
||||
return &x
|
||||
}
|
||||
|
||||
func stringptr(x string) *string {
|
||||
return &x
|
||||
}
|
||||
|
||||
type T1 struct{}
|
||||
type T2 struct{}
|
||||
type T3 struct{}
|
||||
|
||||
type IndirComment struct {
|
||||
T1 T1
|
||||
Comment *string `xml:",comment"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type DirectComment struct {
|
||||
T1 T1
|
||||
Comment string `xml:",comment"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IfaceComment struct {
|
||||
T1 T1
|
||||
Comment interface{} `xml:",comment"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IndirChardata struct {
|
||||
T1 T1
|
||||
Chardata *string `xml:",chardata"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type DirectChardata struct {
|
||||
T1 T1
|
||||
Chardata string `xml:",chardata"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IfaceChardata struct {
|
||||
T1 T1
|
||||
Chardata interface{} `xml:",chardata"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IndirCDATA struct {
|
||||
T1 T1
|
||||
CDATA *string `xml:",cdata"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type DirectCDATA struct {
|
||||
T1 T1
|
||||
CDATA string `xml:",cdata"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IfaceCDATA struct {
|
||||
T1 T1
|
||||
CDATA interface{} `xml:",cdata"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IndirInnerXML struct {
|
||||
T1 T1
|
||||
InnerXML *string `xml:",innerxml"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type DirectInnerXML struct {
|
||||
T1 T1
|
||||
InnerXML string `xml:",innerxml"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IfaceInnerXML struct {
|
||||
T1 T1
|
||||
InnerXML interface{} `xml:",innerxml"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IndirElement struct {
|
||||
T1 T1
|
||||
Element *string
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type DirectElement struct {
|
||||
T1 T1
|
||||
Element string
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IfaceElement struct {
|
||||
T1 T1
|
||||
Element interface{}
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IndirOmitEmpty struct {
|
||||
T1 T1
|
||||
OmitEmpty *string `xml:",omitempty"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type DirectOmitEmpty struct {
|
||||
T1 T1
|
||||
OmitEmpty string `xml:",omitempty"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IfaceOmitEmpty struct {
|
||||
T1 T1
|
||||
OmitEmpty interface{} `xml:",omitempty"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IndirAny struct {
|
||||
T1 T1
|
||||
Any *string `xml:",any"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type DirectAny struct {
|
||||
T1 T1
|
||||
Any string `xml:",any"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
type IfaceAny struct {
|
||||
T1 T1
|
||||
Any interface{} `xml:",any"`
|
||||
T2 T2
|
||||
}
|
||||
|
||||
var (
|
||||
nameAttr = "Sarah"
|
||||
ageAttr = uint(12)
|
||||
@ -398,10 +532,12 @@ var (
|
||||
// please try to make them two-way as well to ensure that
|
||||
// marshaling and unmarshaling are as symmetrical as feasible.
|
||||
var marshalTests = []struct {
|
||||
Value interface{}
|
||||
ExpectXML string
|
||||
MarshalOnly bool
|
||||
UnmarshalOnly bool
|
||||
Value interface{}
|
||||
ExpectXML string
|
||||
MarshalOnly bool
|
||||
MarshalError string
|
||||
UnmarshalOnly bool
|
||||
UnmarshalError string
|
||||
}{
|
||||
// Test nil marshals to nothing
|
||||
{Value: nil, ExpectXML: ``, MarshalOnly: true},
|
||||
@ -1133,6 +1269,382 @@ var marshalTests = []struct {
|
||||
ExpectXML: `<NestedAndCData><A><B></B><B></B></A><![CDATA[test]]></NestedAndCData>`,
|
||||
Value: &NestedAndCData{AB: make([]string, 2), CDATA: "test"},
|
||||
},
|
||||
// Test pointer indirection in various kinds of fields.
|
||||
// https://golang.org/issue/19063
|
||||
{
|
||||
ExpectXML: `<IndirComment><T1></T1><!--hi--><T2></T2></IndirComment>`,
|
||||
Value: &IndirComment{Comment: stringptr("hi")},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirComment><T1></T1><T2></T2></IndirComment>`,
|
||||
Value: &IndirComment{Comment: stringptr("")},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirComment><T1></T1><T2></T2></IndirComment>`,
|
||||
Value: &IndirComment{Comment: nil},
|
||||
MarshalError: "xml: bad type for comment field of xml.IndirComment",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirComment><T1></T1><!--hi--><T2></T2></IndirComment>`,
|
||||
Value: &IndirComment{Comment: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceComment><T1></T1><!--hi--><T2></T2></IfaceComment>`,
|
||||
Value: &IfaceComment{Comment: "hi"},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceComment><T1></T1><!--hi--><T2></T2></IfaceComment>`,
|
||||
Value: &IfaceComment{Comment: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceComment><T1></T1><T2></T2></IfaceComment>`,
|
||||
Value: &IfaceComment{Comment: nil},
|
||||
MarshalError: "xml: bad type for comment field of xml.IfaceComment",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceComment><T1></T1><T2></T2></IfaceComment>`,
|
||||
Value: &IfaceComment{Comment: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectComment><T1></T1><!--hi--><T2></T2></DirectComment>`,
|
||||
Value: &DirectComment{Comment: string("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectComment><T1></T1><T2></T2></DirectComment>`,
|
||||
Value: &DirectComment{Comment: string("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirChardata><T1></T1>hi<T2></T2></IndirChardata>`,
|
||||
Value: &IndirChardata{Chardata: stringptr("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirChardata><T1></T1><![CDATA[hi]]><T2></T2></IndirChardata>`,
|
||||
Value: &IndirChardata{Chardata: stringptr("hi")},
|
||||
UnmarshalOnly: true, // marshals without CDATA
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirChardata><T1></T1><T2></T2></IndirChardata>`,
|
||||
Value: &IndirChardata{Chardata: stringptr("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirChardata><T1></T1><T2></T2></IndirChardata>`,
|
||||
Value: &IndirChardata{Chardata: nil},
|
||||
MarshalOnly: true, // unmarshal leaves Chardata=stringptr("")
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceChardata><T1></T1>hi<T2></T2></IfaceChardata>`,
|
||||
Value: &IfaceChardata{Chardata: string("hi")},
|
||||
UnmarshalError: "cannot unmarshal into interface {}",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceChardata><T1></T1><![CDATA[hi]]><T2></T2></IfaceChardata>`,
|
||||
Value: &IfaceChardata{Chardata: string("hi")},
|
||||
UnmarshalOnly: true, // marshals without CDATA
|
||||
UnmarshalError: "cannot unmarshal into interface {}",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceChardata><T1></T1><T2></T2></IfaceChardata>`,
|
||||
Value: &IfaceChardata{Chardata: string("")},
|
||||
UnmarshalError: "cannot unmarshal into interface {}",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceChardata><T1></T1><T2></T2></IfaceChardata>`,
|
||||
Value: &IfaceChardata{Chardata: nil},
|
||||
UnmarshalError: "cannot unmarshal into interface {}",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectChardata><T1></T1>hi<T2></T2></DirectChardata>`,
|
||||
Value: &DirectChardata{Chardata: string("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectChardata><T1></T1><![CDATA[hi]]><T2></T2></DirectChardata>`,
|
||||
Value: &DirectChardata{Chardata: string("hi")},
|
||||
UnmarshalOnly: true, // marshals without CDATA
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectChardata><T1></T1><T2></T2></DirectChardata>`,
|
||||
Value: &DirectChardata{Chardata: string("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirCDATA><T1></T1><![CDATA[hi]]><T2></T2></IndirCDATA>`,
|
||||
Value: &IndirCDATA{CDATA: stringptr("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirCDATA><T1></T1>hi<T2></T2></IndirCDATA>`,
|
||||
Value: &IndirCDATA{CDATA: stringptr("hi")},
|
||||
UnmarshalOnly: true, // marshals with CDATA
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirCDATA><T1></T1><T2></T2></IndirCDATA>`,
|
||||
Value: &IndirCDATA{CDATA: stringptr("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirCDATA><T1></T1><T2></T2></IndirCDATA>`,
|
||||
Value: &IndirCDATA{CDATA: nil},
|
||||
MarshalOnly: true, // unmarshal leaves CDATA=stringptr("")
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceCDATA><T1></T1><![CDATA[hi]]><T2></T2></IfaceCDATA>`,
|
||||
Value: &IfaceCDATA{CDATA: string("hi")},
|
||||
UnmarshalError: "cannot unmarshal into interface {}",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceCDATA><T1></T1>hi<T2></T2></IfaceCDATA>`,
|
||||
Value: &IfaceCDATA{CDATA: string("hi")},
|
||||
UnmarshalOnly: true, // marshals with CDATA
|
||||
UnmarshalError: "cannot unmarshal into interface {}",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceCDATA><T1></T1><T2></T2></IfaceCDATA>`,
|
||||
Value: &IfaceCDATA{CDATA: string("")},
|
||||
UnmarshalError: "cannot unmarshal into interface {}",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceCDATA><T1></T1><T2></T2></IfaceCDATA>`,
|
||||
Value: &IfaceCDATA{CDATA: nil},
|
||||
UnmarshalError: "cannot unmarshal into interface {}",
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectCDATA><T1></T1><![CDATA[hi]]><T2></T2></DirectCDATA>`,
|
||||
Value: &DirectCDATA{CDATA: string("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectCDATA><T1></T1>hi<T2></T2></DirectCDATA>`,
|
||||
Value: &DirectCDATA{CDATA: string("hi")},
|
||||
UnmarshalOnly: true, // marshals with CDATA
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectCDATA><T1></T1><T2></T2></DirectCDATA>`,
|
||||
Value: &DirectCDATA{CDATA: string("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirInnerXML><T1></T1><hi/><T2></T2></IndirInnerXML>`,
|
||||
Value: &IndirInnerXML{InnerXML: stringptr("<hi/>")},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirInnerXML><T1></T1><T2></T2></IndirInnerXML>`,
|
||||
Value: &IndirInnerXML{InnerXML: stringptr("")},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirInnerXML><T1></T1><T2></T2></IndirInnerXML>`,
|
||||
Value: &IndirInnerXML{InnerXML: nil},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirInnerXML><T1></T1><hi/><T2></T2></IndirInnerXML>`,
|
||||
Value: &IndirInnerXML{InnerXML: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceInnerXML><T1></T1><hi/><T2></T2></IfaceInnerXML>`,
|
||||
Value: &IfaceInnerXML{InnerXML: "<hi/>"},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceInnerXML><T1></T1><hi/><T2></T2></IfaceInnerXML>`,
|
||||
Value: &IfaceInnerXML{InnerXML: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceInnerXML><T1></T1><T2></T2></IfaceInnerXML>`,
|
||||
Value: &IfaceInnerXML{InnerXML: nil},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceInnerXML><T1></T1><T2></T2></IfaceInnerXML>`,
|
||||
Value: &IfaceInnerXML{InnerXML: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectInnerXML><T1></T1><hi/><T2></T2></DirectInnerXML>`,
|
||||
Value: &DirectInnerXML{InnerXML: string("<hi/>")},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectInnerXML><T1></T1><hi/><T2></T2></DirectInnerXML>`,
|
||||
Value: &DirectInnerXML{InnerXML: string("<T1></T1><hi/><T2></T2>")},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectInnerXML><T1></T1><T2></T2></DirectInnerXML>`,
|
||||
Value: &DirectInnerXML{InnerXML: string("")},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectInnerXML><T1></T1><T2></T2></DirectInnerXML>`,
|
||||
Value: &DirectInnerXML{InnerXML: string("<T1></T1><T2></T2>")},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirElement><T1></T1><Element>hi</Element><T2></T2></IndirElement>`,
|
||||
Value: &IndirElement{Element: stringptr("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirElement><T1></T1><Element></Element><T2></T2></IndirElement>`,
|
||||
Value: &IndirElement{Element: stringptr("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirElement><T1></T1><T2></T2></IndirElement>`,
|
||||
Value: &IndirElement{Element: nil},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceElement><T1></T1><Element>hi</Element><T2></T2></IfaceElement>`,
|
||||
Value: &IfaceElement{Element: "hi"},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceElement><T1></T1><Element>hi</Element><T2></T2></IfaceElement>`,
|
||||
Value: &IfaceElement{Element: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceElement><T1></T1><T2></T2></IfaceElement>`,
|
||||
Value: &IfaceElement{Element: nil},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceElement><T1></T1><T2></T2></IfaceElement>`,
|
||||
Value: &IfaceElement{Element: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectElement><T1></T1><Element>hi</Element><T2></T2></DirectElement>`,
|
||||
Value: &DirectElement{Element: string("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectElement><T1></T1><Element></Element><T2></T2></DirectElement>`,
|
||||
Value: &DirectElement{Element: string("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IndirOmitEmpty>`,
|
||||
Value: &IndirOmitEmpty{OmitEmpty: stringptr("hi")},
|
||||
},
|
||||
{
|
||||
// Note: Changed in Go 1.8 to include <OmitEmpty> element (because x.OmitEmpty != nil).
|
||||
ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty></OmitEmpty><T2></T2></IndirOmitEmpty>`,
|
||||
Value: &IndirOmitEmpty{OmitEmpty: stringptr("")},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty></OmitEmpty><T2></T2></IndirOmitEmpty>`,
|
||||
Value: &IndirOmitEmpty{OmitEmpty: stringptr("")},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirOmitEmpty><T1></T1><T2></T2></IndirOmitEmpty>`,
|
||||
Value: &IndirOmitEmpty{OmitEmpty: nil},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IfaceOmitEmpty>`,
|
||||
Value: &IfaceOmitEmpty{OmitEmpty: "hi"},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IfaceOmitEmpty>`,
|
||||
Value: &IfaceOmitEmpty{OmitEmpty: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceOmitEmpty><T1></T1><T2></T2></IfaceOmitEmpty>`,
|
||||
Value: &IfaceOmitEmpty{OmitEmpty: nil},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceOmitEmpty><T1></T1><T2></T2></IfaceOmitEmpty>`,
|
||||
Value: &IfaceOmitEmpty{OmitEmpty: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></DirectOmitEmpty>`,
|
||||
Value: &DirectOmitEmpty{OmitEmpty: string("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectOmitEmpty><T1></T1><T2></T2></DirectOmitEmpty>`,
|
||||
Value: &DirectOmitEmpty{OmitEmpty: string("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirAny><T1></T1><Any>hi</Any><T2></T2></IndirAny>`,
|
||||
Value: &IndirAny{Any: stringptr("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirAny><T1></T1><Any></Any><T2></T2></IndirAny>`,
|
||||
Value: &IndirAny{Any: stringptr("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirAny><T1></T1><T2></T2></IndirAny>`,
|
||||
Value: &IndirAny{Any: nil},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceAny><T1></T1><Any>hi</Any><T2></T2></IfaceAny>`,
|
||||
Value: &IfaceAny{Any: "hi"},
|
||||
MarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceAny><T1></T1><Any>hi</Any><T2></T2></IfaceAny>`,
|
||||
Value: &IfaceAny{Any: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceAny><T1></T1><T2></T2></IfaceAny>`,
|
||||
Value: &IfaceAny{Any: nil},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceAny><T1></T1><T2></T2></IfaceAny>`,
|
||||
Value: &IfaceAny{Any: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectAny><T1></T1><Any>hi</Any><T2></T2></DirectAny>`,
|
||||
Value: &DirectAny{Any: string("hi")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectAny><T1></T1><Any></Any><T2></T2></DirectAny>`,
|
||||
Value: &DirectAny{Any: string("")},
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirFoo><T1></T1><Foo>hi</Foo><T2></T2></IndirFoo>`,
|
||||
Value: &IndirAny{Any: stringptr("hi")},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirFoo><T1></T1><Foo></Foo><T2></T2></IndirFoo>`,
|
||||
Value: &IndirAny{Any: stringptr("")},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IndirFoo><T1></T1><T2></T2></IndirFoo>`,
|
||||
Value: &IndirAny{Any: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceFoo><T1></T1><Foo>hi</Foo><T2></T2></IfaceFoo>`,
|
||||
Value: &IfaceAny{Any: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceFoo><T1></T1><T2></T2></IfaceFoo>`,
|
||||
Value: &IfaceAny{Any: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<IfaceFoo><T1></T1><T2></T2></IfaceFoo>`,
|
||||
Value: &IfaceAny{Any: nil},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectFoo><T1></T1><Foo>hi</Foo><T2></T2></DirectFoo>`,
|
||||
Value: &DirectAny{Any: string("hi")},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
{
|
||||
ExpectXML: `<DirectFoo><T1></T1><Foo></Foo><T2></T2></DirectFoo>`,
|
||||
Value: &DirectAny{Any: string("")},
|
||||
UnmarshalOnly: true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
@ -1142,7 +1654,17 @@ func TestMarshal(t *testing.T) {
|
||||
}
|
||||
data, err := Marshal(test.Value)
|
||||
if err != nil {
|
||||
t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
|
||||
if test.MarshalError == "" {
|
||||
t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(err.Error(), test.MarshalError) {
|
||||
t.Errorf("#%d: marshal(%#v): %s, want %q", idx, test.Value, err, test.MarshalError)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.MarshalError != "" {
|
||||
t.Errorf("#%d: Marshal succeeded, want error %q", idx, test.MarshalError)
|
||||
continue
|
||||
}
|
||||
if got, want := string(data), test.ExpectXML; got != want {
|
||||
@ -1268,8 +1790,16 @@ func TestUnmarshal(t *testing.T) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("#%d: unexpected error: %#v", i, err)
|
||||
} else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
|
||||
if test.UnmarshalError == "" {
|
||||
t.Errorf("#%d: unmarshal(%#v): %s", i, test.ExpectXML, err)
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(err.Error(), test.UnmarshalError) {
|
||||
t.Errorf("#%d: unmarshal(%#v): %s, want %q", i, test.ExpectXML, err, test.UnmarshalError)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ func defaultGOPATH() string {
|
||||
}
|
||||
if home := os.Getenv(env); home != "" {
|
||||
def := filepath.Join(home, "go")
|
||||
if def == runtime.GOROOT() {
|
||||
if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
|
||||
// Don't set the default GOPATH to GOROOT,
|
||||
// as that will trigger warnings from the go tool.
|
||||
return ""
|
||||
|
@ -2481,17 +2481,24 @@ func TestNumMethodOnDDD(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPtrTo(t *testing.T) {
|
||||
// This block of code means that the ptrToThis field of the
|
||||
// reflect data for *unsafe.Pointer is non zero, see
|
||||
// https://golang.org/issue/19003
|
||||
var x unsafe.Pointer
|
||||
var y = &x
|
||||
var z = &y
|
||||
|
||||
var i int
|
||||
|
||||
typ := TypeOf(i)
|
||||
typ := TypeOf(z)
|
||||
for i = 0; i < 100; i++ {
|
||||
typ = PtrTo(typ)
|
||||
}
|
||||
for i = 0; i < 100; i++ {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
if typ != TypeOf(i) {
|
||||
t.Errorf("after 100 PtrTo and Elem, have %s, want %s", typ, TypeOf(i))
|
||||
if typ != TypeOf(z) {
|
||||
t.Errorf("after 100 PtrTo and Elem, have %s, want %s", typ, TypeOf(z))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1174,6 +1174,7 @@ func (t *rtype) ptrTo() *rtype {
|
||||
pp := *prototype
|
||||
|
||||
pp.string = &s
|
||||
pp.ptrToThis = nil
|
||||
|
||||
// For the type structures linked into the binary, the
|
||||
// compiler provides a good hash of the string.
|
||||
|
@ -61,7 +61,7 @@ static void* cpuHogDriver(void* arg __attribute__ ((unused))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void runCPUHogThread() {
|
||||
void runCPUHogThread(void) {
|
||||
pthread_t tid;
|
||||
pthread_create(&tid, 0, cpuHogDriver, 0);
|
||||
}
|
||||
|
@ -15,16 +15,16 @@ package main
|
||||
|
||||
char *p;
|
||||
|
||||
static int f3() {
|
||||
static int f3(void) {
|
||||
*p = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f2() {
|
||||
static int f2(void) {
|
||||
return f3();
|
||||
}
|
||||
|
||||
static int f1() {
|
||||
static int f1(void) {
|
||||
return f2();
|
||||
}
|
||||
|
||||
|
@ -821,6 +821,7 @@ func (m *M) Run() int {
|
||||
haveExamples = len(m.examples) > 0
|
||||
testRan, testOk := runTests(m.deps.MatchString, m.tests)
|
||||
exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
|
||||
stopAlarm()
|
||||
if !testRan && !exampleRan && *matchBenchmarks == "" {
|
||||
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
|
||||
}
|
||||
|
@ -85,6 +85,12 @@ static const String preflection_string =
|
||||
sizeof PREFLECTION - 1,
|
||||
};
|
||||
|
||||
extern const uintptr pointer_unsafe_Pointer_gc[]
|
||||
__asm__ (GOSYM_PREFIX "__go_td_pN14_unsafe.Pointer$gc");
|
||||
|
||||
const uintptr pointer_unsafe_Pointer_gc[] __attribute__((aligned(4))) =
|
||||
{sizeof(void*), GC_APTR, 0, GC_END};
|
||||
|
||||
const struct __go_ptr_type pointer_unsafe_Pointer =
|
||||
{
|
||||
/* __common */
|
||||
@ -104,7 +110,7 @@ const struct __go_ptr_type pointer_unsafe_Pointer =
|
||||
/* __equalfn */
|
||||
&runtime_pointerequal_descriptor,
|
||||
/* __gc */
|
||||
unsafe_Pointer_gc,
|
||||
pointer_unsafe_Pointer_gc,
|
||||
/* __reflection */
|
||||
&preflection_string,
|
||||
/* __uncommon */
|
||||
|
Loading…
x
Reference in New Issue
Block a user