From ea250f561291c184423c4372041c067f5233fa5c Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 17 Apr 2017 22:10:58 +0000 Subject: [PATCH] libgo: update to Go 1.8.1 release Reviewed-on: https://go-review.googlesource.com/40775 From-SVN: r246957 --- libgo/MERGE | 2 +- libgo/VERSION | 2 +- libgo/go/cmd/go/go_test.go | 18 +++++ libgo/go/cmd/go/pkg.go | 4 -- libgo/go/cmd/go/test.go | 21 ++++++ libgo/go/crypto/tls/common.go | 1 + libgo/go/crypto/tls/tls_test.go | 90 ++++++++++++++++++------ libgo/go/encoding/xml/marshal_test.go | 7 +- libgo/go/encoding/xml/read.go | 3 +- libgo/go/encoding/xml/xml_test.go | 34 +++++++++ libgo/go/image/png/reader.go | 5 ++ libgo/go/image/png/reader_test.go | 10 +++ libgo/go/internal/testenv/testenv.go | 9 +++ libgo/go/internal/testenv/testenv_cgo.go | 11 +++ libgo/go/net/http/http.go | 2 +- libgo/go/net/net.go | 2 +- libgo/go/os/exec/exec_test.go | 10 ++- libgo/go/reflect/all_test.go | 35 ++++++++- libgo/go/runtime/crash_unix_test.go | 73 +++++++++++++++++++ libgo/go/runtime/export_test.go | 13 ++++ libgo/go/runtime/runtime1.go | 6 ++ libgo/go/runtime/sema.go | 1 + libgo/go/text/template/multi_test.go | 37 +++++++++- libgo/go/text/template/template.go | 4 +- 24 files changed, 359 insertions(+), 41 deletions(-) create mode 100644 libgo/go/internal/testenv/testenv_cgo.go diff --git a/libgo/MERGE b/libgo/MERGE index 16f346a2b8e..a5808db54ff 100644 --- a/libgo/MERGE +++ b/libgo/MERGE @@ -1,4 +1,4 @@ -cd6b6202dd1559b3ac63179b45f1833fcfbe7eca +a4c18f063b6659079ca2848ca217a0587dabc001 The first line of this file holds the git revision number of the last merge done from the master library sources. diff --git a/libgo/VERSION b/libgo/VERSION index 436edd93930..dce1d463ba1 100644 --- a/libgo/VERSION +++ b/libgo/VERSION @@ -1 +1 @@ -go1.8 +go1.8.1 diff --git a/libgo/go/cmd/go/go_test.go b/libgo/go/cmd/go/go_test.go index 56de65ce55a..fa78578d121 100644 --- a/libgo/go/cmd/go/go_test.go +++ b/libgo/go/cmd/go/go_test.go @@ -2227,6 +2227,24 @@ func TestTestEmpty(t *testing.T) { } } +func TestTestRaceInstall(t *testing.T) { + if !canRace { + t.Skip("no race detector") + } + + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + + tg.tempDir("pkg") + pkgdir := tg.path("pkg") + tg.run("install", "-race", "-pkgdir="+pkgdir, "std") + tg.run("test", "-race", "-pkgdir="+pkgdir, "-i", "-v", "empty/pkg") + if tg.getStderr() != "" { + t.Error("go test -i -race: rebuilds cached packages") + } +} + func TestBuildDryRunWithCgo(t *testing.T) { if !canCgo { t.Skip("skipping because cgo not enabled") diff --git a/libgo/go/cmd/go/pkg.go b/libgo/go/cmd/go/pkg.go index 61c1b9960ea..f0dc95670f9 100644 --- a/libgo/go/cmd/go/pkg.go +++ b/libgo/go/cmd/go/pkg.go @@ -970,10 +970,6 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package if p.Name == "main" && goarch == "arm" { importPaths = append(importPaths, "math") } - // In coverage atomic mode everything depends on sync/atomic. - if testCoverMode == "atomic" && (!p.Standard || (p.ImportPath != "runtime/cgo" && p.ImportPath != "runtime/race" && p.ImportPath != "sync/atomic")) { - importPaths = append(importPaths, "sync/atomic") - } } // Runtime and its internal packages depend on runtime/internal/sys, diff --git a/libgo/go/cmd/go/test.go b/libgo/go/cmd/go/test.go index 5c13b653f01..12990feb43c 100644 --- a/libgo/go/cmd/go/test.go +++ b/libgo/go/cmd/go/test.go @@ -548,6 +548,10 @@ func runTest(cmd *Command, args []string) { // Prepare build + run + print actions for all packages being tested. for _, p := range pkgs { + // sync/atomic import is inserted by the cover tool. See #18486 + if testCover && testCoverMode == "atomic" { + ensureImport(p, "sync/atomic") + } buildTest, runTest, printTest, err := b.test(p) if err != nil { str := err.Error() @@ -639,6 +643,23 @@ func runTest(cmd *Command, args []string) { b.do(root) } +// ensures that package p imports the named package. +func ensureImport(p *Package, pkg string) { + for _, d := range p.deps { + if d.Name == pkg { + return + } + } + + a := loadPackage(pkg, &importStack{}) + if a.Error != nil { + fatalf("load %s: %v", pkg, a.Error) + } + computeStale(a) + + p.imports = append(p.imports, a) +} + func contains(x []string, s string) bool { for _, t := range x { if t == s { diff --git a/libgo/go/crypto/tls/common.go b/libgo/go/crypto/tls/common.go index 276d1761ea0..de833a90563 100644 --- a/libgo/go/crypto/tls/common.go +++ b/libgo/go/crypto/tls/common.go @@ -563,6 +563,7 @@ func (c *Config) Clone() *Config { Certificates: c.Certificates, NameToCertificate: c.NameToCertificate, GetCertificate: c.GetCertificate, + GetClientCertificate: c.GetClientCertificate, GetConfigForClient: c.GetConfigForClient, VerifyPeerCertificate: c.VerifyPeerCertificate, RootCAs: c.RootCAs, diff --git a/libgo/go/crypto/tls/tls_test.go b/libgo/go/crypto/tls/tls_test.go index 8933f4f2015..86812f0c974 100644 --- a/libgo/go/crypto/tls/tls_test.go +++ b/libgo/go/crypto/tls/tls_test.go @@ -13,13 +13,11 @@ import ( "io" "io/ioutil" "math" - "math/rand" "net" "os" "reflect" "strings" "testing" - "testing/quick" "time" ) @@ -568,11 +566,50 @@ func TestConnCloseWrite(t *testing.T) { } } -func TestClone(t *testing.T) { +func TestCloneFuncFields(t *testing.T) { + const expectedCount = 5 + called := 0 + + c1 := Config{ + Time: func() time.Time { + called |= 1 << 0 + return time.Time{} + }, + GetCertificate: func(*ClientHelloInfo) (*Certificate, error) { + called |= 1 << 1 + return nil, nil + }, + GetClientCertificate: func(*CertificateRequestInfo) (*Certificate, error) { + called |= 1 << 2 + return nil, nil + }, + GetConfigForClient: func(*ClientHelloInfo) (*Config, error) { + called |= 1 << 3 + return nil, nil + }, + VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + called |= 1 << 4 + return nil + }, + } + + c2 := c1.Clone() + + c2.Time() + c2.GetCertificate(nil) + c2.GetClientCertificate(nil) + c2.GetConfigForClient(nil) + c2.VerifyPeerCertificate(nil, nil) + + if called != (1<`, true}, + {``, true}, + {``, true}, + {``, true}, + {``, false}, + {``, false}, + {`bad`, false}, + } + + for _, tt := range tests { + err := Unmarshal([]byte(tt.input), new(X)) + if tt.ok { + if err != nil { + t.Errorf("%s: unexpected error: %v", tt.input, err) + } + } else { + if err == nil { + t.Errorf("%s: unexpected success", tt.input) + } + } + } +} diff --git a/libgo/go/image/png/reader.go b/libgo/go/image/png/reader.go index 32f78f0ffe8..8299df56735 100644 --- a/libgo/go/image/png/reader.go +++ b/libgo/go/image/png/reader.go @@ -612,6 +612,11 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image } } case cbG8: + if d.useTransparent { + // Match error from Go 1.7 and earlier. + // Go 1.9 will decode this properly. + return nil, chunkOrderError + } copy(gray.Pix[pixOffset:], cdat) pixOffset += gray.Stride case cbGA8: diff --git a/libgo/go/image/png/reader_test.go b/libgo/go/image/png/reader_test.go index b9e9f4d02c6..503b5dc567b 100644 --- a/libgo/go/image/png/reader_test.go +++ b/libgo/go/image/png/reader_test.go @@ -629,3 +629,13 @@ func BenchmarkDecodeRGB(b *testing.B) { func BenchmarkDecodeInterlacing(b *testing.B) { benchmarkDecode(b, "testdata/benchRGB-interlace.png", 4) } + +func TestIssue19553(t *testing.T) { + var buf = []byte{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x85, 0x2c, 0x88, 0x80, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52, 0x4e, 0x53, 0x00, 0xff, 0x5b, 0x91, 0x22, 0xb5, 0x00, 0x00, 0x00, 0x02, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x0a, 0xf0, 0x01, 0x42, 0xac, 0x34, 0x98, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd5, 0x04, 0x02, 0x12, 0x11, 0x11, 0xf7, 0x65, 0x3d, 0x8b, 0x00, 0x00, 0x00, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff, 0xff, 0xb9, 0xbd, 0x70, 0xf0, 0x8c, 0x01, 0xc8, 0xaf, 0x6e, 0x99, 0x02, 0x05, 0xd9, 0x7b, 0xc1, 0xfc, 0x6b, 0xff, 0xa1, 0xa0, 0x87, 0x30, 0xff, 0xd9, 0xde, 0xbd, 0xd5, 0x4b, 0xf7, 0xee, 0xfd, 0x0e, 0xe3, 0xef, 0xcd, 0x06, 0x19, 0x14, 0xf5, 0x1e, 0xce, 0xef, 0x01, 0x31, 0x92, 0xd7, 0x82, 0x41, 0x31, 0x9c, 0x3f, 0x07, 0x02, 0xee, 0xa1, 0xaa, 0xff, 0xff, 0x9f, 0xe1, 0xd9, 0x56, 0x30, 0xf8, 0x0e, 0xe5, 0x03, 0x00, 0xa9, 0x42, 0x84, 0x3d, 0xdf, 0x8f, 0xa6, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, + } + _, err := Decode(bytes.NewReader(buf)) + if err != chunkOrderError { + t.Errorf("Decode: expected chunkOrderError for transparent gray8, got %v", err) + } +} diff --git a/libgo/go/internal/testenv/testenv.go b/libgo/go/internal/testenv/testenv.go index fdba8b25c3a..71a692b367e 100644 --- a/libgo/go/internal/testenv/testenv.go +++ b/libgo/go/internal/testenv/testenv.go @@ -141,6 +141,15 @@ func MustHaveExternalNetwork(t *testing.T) { } } +var haveCGO bool + +// MustHaveCGO calls t.Skip if cgo is not available. +func MustHaveCGO(t *testing.T) { + if !haveCGO { + t.Skipf("skipping test: no cgo") + } +} + // HasSymlink reports whether the current system can use os.Symlink. func HasSymlink() bool { ok, _ := hasSymlink() diff --git a/libgo/go/internal/testenv/testenv_cgo.go b/libgo/go/internal/testenv/testenv_cgo.go new file mode 100644 index 00000000000..e3d4d16b33e --- /dev/null +++ b/libgo/go/internal/testenv/testenv_cgo.go @@ -0,0 +1,11 @@ +// Copyright 2017 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. + +// +build cgo + +package testenv + +func init() { + haveCGO = true +} diff --git a/libgo/go/net/http/http.go b/libgo/go/net/http/http.go index 826f7ff3da5..b95ca89f409 100644 --- a/libgo/go/net/http/http.go +++ b/libgo/go/net/http/http.go @@ -20,7 +20,7 @@ const maxInt64 = 1<<63 - 1 // aLongTimeAgo is a non-zero time, far in the past, used for // immediate cancelation of network operations. -var aLongTimeAgo = time.Unix(233431200, 0) +var aLongTimeAgo = time.Unix(1, 0) // TODO(bradfitz): move common stuff here. The other files have accumulated // generic http stuff in random places. diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index 81206ea1cb6..a8b57361e6c 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/net/net.go @@ -468,7 +468,7 @@ func (e *OpError) Error() string { var ( // aLongTimeAgo is a non-zero time, far in the past, used for // immediate cancelation of dials. - aLongTimeAgo = time.Unix(233431200, 0) + aLongTimeAgo = time.Unix(1, 0) // nonDeadline and noCancel are just zero values for // readability with functions taking too many parameters. diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go index a9cceecd2a8..f13635138a1 100644 --- a/libgo/go/os/exec/exec_test.go +++ b/libgo/go/os/exec/exec_test.go @@ -266,9 +266,13 @@ func TestStdinCloseRace(t *testing.T) { t.Fatalf("Start: %v", err) } go func() { - if err := cmd.Process.Kill(); err != nil { - t.Errorf("Kill: %v", err) - } + // We don't check the error return of Kill. It is + // possible that the process has already exited, in + // which case Kill will return an error "process + // already finished". The purpose of this test is to + // see whether the race detector reports an error; it + // doesn't matter whether this Kill succeeds or not. + cmd.Process.Kill() }() go func() { // Send the wrong string, so that the child fails even diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 859de7c8670..3686167ed42 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -1681,6 +1681,11 @@ func (p Point) GCMethod(k int) int { } // This will be index 3. +func (p Point) NoArgs() { + // Exercise no-argument/no-result paths. +} + +// This will be index 4. func (p Point) TotalDist(points ...Point) int { tot := 0 for _, q := range points { @@ -1709,6 +1714,15 @@ func TestMethod(t *testing.T) { t.Errorf("Type MethodByName returned %d; want 275", i) } + m, ok = TypeOf(p).MethodByName("NoArgs") + if !ok { + t.Fatalf("method by name failed") + } + n := len(m.Func.Call([]Value{ValueOf(p)})) + if n != 0 { + t.Errorf("NoArgs returned %d values; want 0", n) + } + i = TypeOf(&p).Method(1).Func.Call([]Value{ValueOf(&p), ValueOf(12)})[0].Int() if i != 300 { t.Errorf("Pointer Type Method returned %d; want 300", i) @@ -1723,6 +1737,15 @@ func TestMethod(t *testing.T) { t.Errorf("Pointer Type MethodByName returned %d; want 325", i) } + m, ok = TypeOf(&p).MethodByName("NoArgs") + if !ok { + t.Fatalf("method by name failed") + } + n = len(m.Func.Call([]Value{ValueOf(&p)})) + if n != 0 { + t.Errorf("NoArgs returned %d values; want 0", n) + } + // Curried method of value. tfunc := TypeOf((func(int) int)(nil)) v := ValueOf(p).Method(1) @@ -1741,6 +1764,8 @@ func TestMethod(t *testing.T) { if i != 375 { t.Errorf("Value MethodByName returned %d; want 375", i) } + v = ValueOf(p).MethodByName("NoArgs") + v.Call(nil) // Curried method of pointer. v = ValueOf(&p).Method(1) @@ -1759,6 +1784,8 @@ func TestMethod(t *testing.T) { if i != 425 { t.Errorf("Pointer Value MethodByName returned %d; want 425", i) } + v = ValueOf(&p).MethodByName("NoArgs") + v.Call(nil) // Curried method of interface value. // Have to wrap interface value in a struct to get at it. @@ -1808,6 +1835,9 @@ func TestMethodValue(t *testing.T) { if i != 275 { t.Errorf("Value MethodByName returned %d; want 275", i) } + v = ValueOf(p).MethodByName("NoArgs") + ValueOf(v.Interface()).Call(nil) + v.Interface().(func())() // Curried method of pointer. v = ValueOf(&p).Method(1) @@ -1826,6 +1856,9 @@ func TestMethodValue(t *testing.T) { if i != 325 { t.Errorf("Pointer Value MethodByName returned %d; want 325", i) } + v = ValueOf(&p).MethodByName("NoArgs") + ValueOf(v.Interface()).Call(nil) + v.Interface().(func())() // Curried method of pointer to pointer. pp := &p @@ -1881,7 +1914,7 @@ func TestVariadicMethodValue(t *testing.T) { // Curried method of value. tfunc := TypeOf((func(...Point) int)(nil)) - v := ValueOf(p).Method(3) + v := ValueOf(p).Method(4) if tt := v.Type(); tt != tfunc { t.Errorf("Variadic Method Type is %s; want %s", tt, tfunc) } diff --git a/libgo/go/runtime/crash_unix_test.go b/libgo/go/runtime/crash_unix_test.go index 97deed8b9d9..182c84b6392 100644 --- a/libgo/go/runtime/crash_unix_test.go +++ b/libgo/go/runtime/crash_unix_test.go @@ -9,6 +9,7 @@ package runtime_test import ( "bytes" "internal/testenv" + "io" "io/ioutil" "os" "os/exec" @@ -153,6 +154,78 @@ func loop(i int, c chan bool) { } ` +func TestPanicSystemstack(t *testing.T) { + // Test that GOTRACEBACK=crash prints both the system and user + // stack of other threads. + + // The GOTRACEBACK=crash handler takes 0.1 seconds even if + // it's not writing a core file and potentially much longer if + // it is. Skip in short mode. + if testing.Short() { + t.Skip("Skipping in short mode (GOTRACEBACK=crash is slow)") + } + + t.Parallel() + cmd := exec.Command(os.Args[0], "testPanicSystemstackInternal") + cmd = testEnv(cmd) + cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") + pr, pw, err := os.Pipe() + if err != nil { + t.Fatal("creating pipe: ", err) + } + cmd.Stderr = pw + if err := cmd.Start(); err != nil { + t.Fatal("starting command: ", err) + } + defer cmd.Process.Wait() + defer cmd.Process.Kill() + if err := pw.Close(); err != nil { + t.Log("closing write pipe: ", err) + } + defer pr.Close() + + // Wait for "x\nx\n" to indicate readiness. + buf := make([]byte, 4) + _, err = io.ReadFull(pr, buf) + if err != nil || string(buf) != "x\nx\n" { + t.Fatal("subprocess failed; output:\n", string(buf)) + } + + // Send SIGQUIT. + if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil { + t.Fatal("signaling subprocess: ", err) + } + + // Get traceback. + tb, err := ioutil.ReadAll(pr) + if err != nil { + t.Fatal("reading traceback from pipe: ", err) + } + + // Traceback should have two testPanicSystemstackInternal's + // and two blockOnSystemStackInternal's. + if bytes.Count(tb, []byte("testPanicSystemstackInternal")) != 2 { + t.Fatal("traceback missing user stack:\n", string(tb)) + } else if bytes.Count(tb, []byte("blockOnSystemStackInternal")) != 2 { + t.Fatal("traceback missing system stack:\n", string(tb)) + } +} + +func init() { + if len(os.Args) >= 2 && os.Args[1] == "testPanicSystemstackInternal" { + // Get two threads running on the system stack with + // something recognizable in the stack trace. + runtime.GOMAXPROCS(2) + go testPanicSystemstackInternal() + testPanicSystemstackInternal() + } +} + +func testPanicSystemstackInternal() { + runtime.BlockOnSystemStack() + os.Exit(1) // Should be unreachable. +} + func TestSignalExitStatus(t *testing.T) { testenv.MustHaveGoBuild(t) exe, err := buildTestProg(t, "testprog") diff --git a/libgo/go/runtime/export_test.go b/libgo/go/runtime/export_test.go index fcc1e6199a4..cc4b188b60c 100644 --- a/libgo/go/runtime/export_test.go +++ b/libgo/go/runtime/export_test.go @@ -243,3 +243,16 @@ func CountPagesInUse() (pagesInUse, counted uintptr) { return } */ + +// BlockOnSystemStack switches to the system stack, prints "x\n" to +// stderr, and blocks in a stack containing +// "runtime.blockOnSystemStackInternal". +func BlockOnSystemStack() { + systemstack(blockOnSystemStackInternal) +} + +func blockOnSystemStackInternal() { + print("x\n") + lock(&deadlock) + lock(&deadlock) +} diff --git a/libgo/go/runtime/runtime1.go b/libgo/go/runtime/runtime1.go index a41cfc81181..99c0f11930f 100644 --- a/libgo/go/runtime/runtime1.go +++ b/libgo/go/runtime/runtime1.go @@ -280,6 +280,12 @@ func check() { throw("atomicor8") } + m = [4]byte{0xff, 0xff, 0xff, 0xff} + atomic.And8(&m[1], 0x1) + if m[0] != 0xff || m[1] != 0x1 || m[2] != 0xff || m[3] != 0xff { + throw("atomicand8") + } + *(*uint64)(unsafe.Pointer(&j)) = ^uint64(0) if j == j { throw("float64nan") diff --git a/libgo/go/runtime/sema.go b/libgo/go/runtime/sema.go index 576a1fb7a20..37318ff9d55 100644 --- a/libgo/go/runtime/sema.go +++ b/libgo/go/runtime/sema.go @@ -171,6 +171,7 @@ func semrelease(addr *uint32) { for x := root.head; x != nil; x = x.next { if x.elem == unsafe.Pointer(addr) { x.acquiretime = t0 + break } } mutexevent(t0-s.acquiretime, 3) diff --git a/libgo/go/text/template/multi_test.go b/libgo/go/text/template/multi_test.go index 8142f008fdf..5d8c08f06f1 100644 --- a/libgo/go/text/template/multi_test.go +++ b/libgo/go/text/template/multi_test.go @@ -363,7 +363,7 @@ func TestEmptyTemplate(t *testing.T) { {[]string{"{{.}}", ""}, "twice", ""}, } - for _, c := range cases { + for i, c := range cases { root := New("root") var ( @@ -378,10 +378,43 @@ func TestEmptyTemplate(t *testing.T) { } buf := &bytes.Buffer{} if err := m.Execute(buf, c.in); err != nil { - t.Fatal(err) + t.Error(i, err) + continue } if buf.String() != c.want { t.Errorf("expected string %q: got %q", c.want, buf.String()) } } } + +// Issue 19249 was a regression in 1.8 caused by the handling of empty +// templates added in that release, which got different answers depending +// on the order templates appeared in the internal map. +func TestIssue19294(t *testing.T) { + // The empty block in "xhtml" should be replaced during execution + // by the contents of "stylesheet", but if the internal map associating + // names with templates is built in the wrong order, the empty block + // looks non-empty and this doesn't happen. + var inlined = map[string]string{ + "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`, + "xhtml": `{{block "stylesheet" .}}{{end}}`, + } + all := []string{"stylesheet", "xhtml"} + for i := 0; i < 100; i++ { + res, err := New("title.xhtml").Parse(`{{template "xhtml" .}}`) + if err != nil { + t.Fatal(err) + } + for _, name := range all { + _, err := res.New(name).Parse(inlined[name]) + if err != nil { + t.Fatal(err) + } + } + var buf bytes.Buffer + res.Execute(&buf, 0) + if buf.String() != "stylesheet" { + t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet") + } + } +} diff --git a/libgo/go/text/template/template.go b/libgo/go/text/template/template.go index b6fceb1795c..3b4f34b4db0 100644 --- a/libgo/go/text/template/template.go +++ b/libgo/go/text/template/template.go @@ -127,7 +127,7 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error // Even if nt == t, we need to install it in the common.tmpl map. if replace, err := t.associate(nt, tree); err != nil { return nil, err - } else if replace { + } else if replace || nt.Tree == nil { nt.Tree = tree } return nt, nil @@ -215,7 +215,7 @@ func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) { if new.common != t.common { panic("internal error: associate not common") } - if t.tmpl[new.name] != nil && parse.IsEmptyTree(tree.Root) && t.Tree != nil { + if old := t.tmpl[new.name]; old != nil && parse.IsEmptyTree(tree.Root) && old.Tree != nil { // If a template by that name exists, // don't replace it with an empty template. return false, nil -- 2.30.2