diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-07-27 22:27:54 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-08-01 11:21:40 -0700 |
commit | f75af8c1464e948b5e166cf5ab09ebf0d82fc253 (patch) | |
tree | 3ba3299859b504bdeb477727471216bd094a0191 /libgo/go/text | |
parent | 75a23e59031fe673fc3b2e60fd1fe5f4c70ecb85 (diff) | |
download | gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.zip gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.gz gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.bz2 |
libgo: update to go1.15rc1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/245157
Diffstat (limited to 'libgo/go/text')
-rw-r--r-- | libgo/go/text/template/exec_test.go | 6 | ||||
-rw-r--r-- | libgo/go/text/template/funcs.go | 217 | ||||
-rw-r--r-- | libgo/go/text/template/link_test.go | 64 | ||||
-rw-r--r-- | libgo/go/text/template/multi_test.go | 3 | ||||
-rw-r--r-- | libgo/go/text/template/parse/node.go | 2 | ||||
-rw-r--r-- | libgo/go/text/template/template.go | 2 |
6 files changed, 186 insertions, 108 deletions
diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go index f92ac6f..be7147a 100644 --- a/libgo/go/text/template/exec_test.go +++ b/libgo/go/text/template/exec_test.go @@ -911,9 +911,9 @@ func TestJSEscaping(t *testing.T) { {`Go "jump" \`, `Go \"jump\" \\`}, {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, {"unprintable \uFDFF", `unprintable \uFDFF`}, - {`<html>`, `\x3Chtml\x3E`}, - {`no = in attributes`, `no \x3D in attributes`}, - {`' does not become HTML entity`, `\x26#x27; does not become HTML entity`}, + {`<html>`, `\u003Chtml\u003E`}, + {`no = in attributes`, `no \u003D in attributes`}, + {`' does not become HTML entity`, `\u0026#x27; does not become HTML entity`}, } for _, tc := range testCases { s := JSEscapeString(tc.in) diff --git a/libgo/go/text/template/funcs.go b/libgo/go/text/template/funcs.go index 46125bc..1b6940a 100644 --- a/libgo/go/text/template/funcs.go +++ b/libgo/go/text/template/funcs.go @@ -12,6 +12,7 @@ import ( "net/url" "reflect" "strings" + "sync" "unicode" "unicode/utf8" ) @@ -29,31 +30,49 @@ import ( // type can return interface{} or reflect.Value. type FuncMap map[string]interface{} -var builtins = FuncMap{ - "and": and, - "call": call, - "html": HTMLEscaper, - "index": index, - "slice": slice, - "js": JSEscaper, - "len": length, - "not": not, - "or": or, - "print": fmt.Sprint, - "printf": fmt.Sprintf, - "println": fmt.Sprintln, - "urlquery": URLQueryEscaper, - - // Comparisons - "eq": eq, // == - "ge": ge, // >= - "gt": gt, // > - "le": le, // <= - "lt": lt, // < - "ne": ne, // != -} - -var builtinFuncs = createValueFuncs(builtins) +// builtins returns the FuncMap. +// It is not a global variable so the linker can dead code eliminate +// more when this isn't called. See golang.org/issue/36021. +// TODO: revert this back to a global map once golang.org/issue/2559 is fixed. +func builtins() FuncMap { + return FuncMap{ + "and": and, + "call": call, + "html": HTMLEscaper, + "index": index, + "slice": slice, + "js": JSEscaper, + "len": length, + "not": not, + "or": or, + "print": fmt.Sprint, + "printf": fmt.Sprintf, + "println": fmt.Sprintln, + "urlquery": URLQueryEscaper, + + // Comparisons + "eq": eq, // == + "ge": ge, // >= + "gt": gt, // > + "le": le, // <= + "lt": lt, // < + "ne": ne, // != + } +} + +var builtinFuncsOnce struct { + sync.Once + v map[string]reflect.Value +} + +// builtinFuncsOnce lazily computes & caches the builtinFuncs map. +// TODO: revert this back to a global map once golang.org/issue/2559 is fixed. +func builtinFuncs() map[string]reflect.Value { + builtinFuncsOnce.Do(func() { + builtinFuncsOnce.v = createValueFuncs(builtins()) + }) + return builtinFuncsOnce.v +} // createValueFuncs turns a FuncMap into a map[string]reflect.Value func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { @@ -125,7 +144,7 @@ func findFunction(name string, tmpl *Template) (reflect.Value, bool) { return fn, true } } - if fn := builtinFuncs[name]; fn.IsValid() { + if fn := builtinFuncs()[name]; fn.IsValid() { return fn, true } return reflect.Value{}, false @@ -185,41 +204,41 @@ func indexArg(index reflect.Value, cap int) (int, error) { // arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each // indexed item must be a map, slice, or array. func index(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) { - v := indirectInterface(item) - if !v.IsValid() { + item = indirectInterface(item) + if !item.IsValid() { return reflect.Value{}, fmt.Errorf("index of untyped nil") } - for _, i := range indexes { - index := indirectInterface(i) + for _, index := range indexes { + index = indirectInterface(index) var isNil bool - if v, isNil = indirect(v); isNil { + if item, isNil = indirect(item); isNil { return reflect.Value{}, fmt.Errorf("index of nil pointer") } - switch v.Kind() { + switch item.Kind() { case reflect.Array, reflect.Slice, reflect.String: - x, err := indexArg(index, v.Len()) + x, err := indexArg(index, item.Len()) if err != nil { return reflect.Value{}, err } - v = v.Index(x) + item = item.Index(x) case reflect.Map: - index, err := prepareArg(index, v.Type().Key()) + index, err := prepareArg(index, item.Type().Key()) if err != nil { return reflect.Value{}, err } - if x := v.MapIndex(index); x.IsValid() { - v = x + if x := item.MapIndex(index); x.IsValid() { + item = x } else { - v = reflect.Zero(v.Type().Elem()) + item = reflect.Zero(item.Type().Elem()) } case reflect.Invalid: - // the loop holds invariant: v.IsValid() + // the loop holds invariant: item.IsValid() panic("unreachable") default: - return reflect.Value{}, fmt.Errorf("can't index item of type %s", v.Type()) + return reflect.Value{}, fmt.Errorf("can't index item of type %s", item.Type()) } } - return v, nil + return item, nil } // Slicing. @@ -229,29 +248,27 @@ func index(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) // is x[:], "slice x 1" is x[1:], and "slice x 1 2 3" is x[1:2:3]. The first // argument must be a string, slice, or array. func slice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) { - var ( - cap int - v = indirectInterface(item) - ) - if !v.IsValid() { + item = indirectInterface(item) + if !item.IsValid() { return reflect.Value{}, fmt.Errorf("slice of untyped nil") } if len(indexes) > 3 { return reflect.Value{}, fmt.Errorf("too many slice indexes: %d", len(indexes)) } - switch v.Kind() { + var cap int + switch item.Kind() { case reflect.String: if len(indexes) == 3 { return reflect.Value{}, fmt.Errorf("cannot 3-index slice a string") } - cap = v.Len() + cap = item.Len() case reflect.Array, reflect.Slice: - cap = v.Cap() + cap = item.Cap() default: - return reflect.Value{}, fmt.Errorf("can't slice item of type %s", v.Type()) + return reflect.Value{}, fmt.Errorf("can't slice item of type %s", item.Type()) } // set default values for cases item[:], item[i:]. - idx := [3]int{0, v.Len()} + idx := [3]int{0, item.Len()} for i, index := range indexes { x, err := indexArg(index, cap) if err != nil { @@ -264,32 +281,28 @@ func slice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[0], idx[1]) } if len(indexes) < 3 { - return v.Slice(idx[0], idx[1]), nil + return item.Slice(idx[0], idx[1]), nil } // given item[i:j:k], make sure i <= j <= k. if idx[1] > idx[2] { return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[1], idx[2]) } - return v.Slice3(idx[0], idx[1], idx[2]), nil + return item.Slice3(idx[0], idx[1], idx[2]), nil } // Length // length returns the length of the item, with an error if it has no defined length. -func length(item interface{}) (int, error) { - v := reflect.ValueOf(item) - if !v.IsValid() { - return 0, fmt.Errorf("len of untyped nil") - } - v, isNil := indirect(v) +func length(item reflect.Value) (int, error) { + item, isNil := indirect(item) if isNil { return 0, fmt.Errorf("len of nil pointer") } - switch v.Kind() { + switch item.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: - return v.Len(), nil + return item.Len(), nil } - return 0, fmt.Errorf("len of type %s", v.Type()) + return 0, fmt.Errorf("len of type %s", item.Type()) } // Function invocation @@ -297,11 +310,11 @@ func length(item interface{}) (int, error) { // call returns the result of evaluating the first argument as a function. // The function must return 1 result, or 2 results, the second of which is an error. func call(fn reflect.Value, args ...reflect.Value) (reflect.Value, error) { - v := indirectInterface(fn) - if !v.IsValid() { + fn = indirectInterface(fn) + if !fn.IsValid() { return reflect.Value{}, fmt.Errorf("call of nil") } - typ := v.Type() + typ := fn.Type() if typ.Kind() != reflect.Func { return reflect.Value{}, fmt.Errorf("non-function of type %s", typ) } @@ -322,7 +335,7 @@ func call(fn reflect.Value, args ...reflect.Value) (reflect.Value, error) { } argv := make([]reflect.Value, len(args)) for i, arg := range args { - value := indirectInterface(arg) + arg = indirectInterface(arg) // Compute the expected type. Clumsy because of variadics. argType := dddType if !typ.IsVariadic() || i < numIn-1 { @@ -330,11 +343,11 @@ func call(fn reflect.Value, args ...reflect.Value) (reflect.Value, error) { } var err error - if argv[i], err = prepareArg(value, argType); err != nil { + if argv[i], err = prepareArg(arg, argType); err != nil { return reflect.Value{}, fmt.Errorf("arg %d: %s", i, err) } } - return safeCall(v, argv) + return safeCall(fn, argv) } // safeCall runs fun.Call(args), and returns the resulting value and error, if @@ -440,52 +453,52 @@ func basicKind(v reflect.Value) (kind, error) { // eq evaluates the comparison a == b || a == c || ... func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { - v1 := indirectInterface(arg1) - if v1 != zero { - if t1 := v1.Type(); !t1.Comparable() { - return false, fmt.Errorf("uncomparable type %s: %v", t1, v1) + arg1 = indirectInterface(arg1) + if arg1 != zero { + if t1 := arg1.Type(); !t1.Comparable() { + return false, fmt.Errorf("uncomparable type %s: %v", t1, arg1) } } if len(arg2) == 0 { return false, errNoComparison } - k1, _ := basicKind(v1) + k1, _ := basicKind(arg1) for _, arg := range arg2 { - v2 := indirectInterface(arg) - k2, _ := basicKind(v2) + arg = indirectInterface(arg) + k2, _ := basicKind(arg) truth := false if k1 != k2 { // Special case: Can compare integer values regardless of type's sign. switch { case k1 == intKind && k2 == uintKind: - truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint() + truth = arg1.Int() >= 0 && uint64(arg1.Int()) == arg.Uint() case k1 == uintKind && k2 == intKind: - truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int()) + truth = arg.Int() >= 0 && arg1.Uint() == uint64(arg.Int()) default: return false, errBadComparison } } else { switch k1 { case boolKind: - truth = v1.Bool() == v2.Bool() + truth = arg1.Bool() == arg.Bool() case complexKind: - truth = v1.Complex() == v2.Complex() + truth = arg1.Complex() == arg.Complex() case floatKind: - truth = v1.Float() == v2.Float() + truth = arg1.Float() == arg.Float() case intKind: - truth = v1.Int() == v2.Int() + truth = arg1.Int() == arg.Int() case stringKind: - truth = v1.String() == v2.String() + truth = arg1.String() == arg.String() case uintKind: - truth = v1.Uint() == v2.Uint() + truth = arg1.Uint() == arg.Uint() default: - if v2 == zero { - truth = v1 == v2 + if arg == zero { + truth = arg1 == arg } else { - if t2 := v2.Type(); !t2.Comparable() { - return false, fmt.Errorf("uncomparable type %s: %v", t2, v2) + if t2 := arg.Type(); !t2.Comparable() { + return false, fmt.Errorf("uncomparable type %s: %v", t2, arg) } - truth = v1.Interface() == v2.Interface() + truth = arg1.Interface() == arg.Interface() } } } @@ -505,13 +518,13 @@ func ne(arg1, arg2 reflect.Value) (bool, error) { // lt evaluates the comparison a < b. func lt(arg1, arg2 reflect.Value) (bool, error) { - v1 := indirectInterface(arg1) - k1, err := basicKind(v1) + arg1 = indirectInterface(arg1) + k1, err := basicKind(arg1) if err != nil { return false, err } - v2 := indirectInterface(arg2) - k2, err := basicKind(v2) + arg2 = indirectInterface(arg2) + k2, err := basicKind(arg2) if err != nil { return false, err } @@ -520,9 +533,9 @@ func lt(arg1, arg2 reflect.Value) (bool, error) { // Special case: Can compare integer values regardless of type's sign. switch { case k1 == intKind && k2 == uintKind: - truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() + truth = arg1.Int() < 0 || uint64(arg1.Int()) < arg2.Uint() case k1 == uintKind && k2 == intKind: - truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int()) + truth = arg2.Int() >= 0 && arg1.Uint() < uint64(arg2.Int()) default: return false, errBadComparison } @@ -531,13 +544,13 @@ func lt(arg1, arg2 reflect.Value) (bool, error) { case boolKind, complexKind: return false, errBadComparisonType case floatKind: - truth = v1.Float() < v2.Float() + truth = arg1.Float() < arg2.Float() case intKind: - truth = v1.Int() < v2.Int() + truth = arg1.Int() < arg2.Int() case stringKind: - truth = v1.String() < v2.String() + truth = arg1.String() < arg2.String() case uintKind: - truth = v1.Uint() < v2.Uint() + truth = arg1.Uint() < arg2.Uint() default: panic("invalid kind") } @@ -640,10 +653,10 @@ var ( jsBackslash = []byte(`\\`) jsApos = []byte(`\'`) jsQuot = []byte(`\"`) - jsLt = []byte(`\x3C`) - jsGt = []byte(`\x3E`) - jsAmp = []byte(`\x26`) - jsEq = []byte(`\x3D`) + jsLt = []byte(`\u003C`) + jsGt = []byte(`\u003E`) + jsAmp = []byte(`\u0026`) + jsEq = []byte(`\u003D`) ) // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. diff --git a/libgo/go/text/template/link_test.go b/libgo/go/text/template/link_test.go new file mode 100644 index 0000000..4eac7e6 --- /dev/null +++ b/libgo/go/text/template/link_test.go @@ -0,0 +1,64 @@ +// 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 template_test + +import ( + "bytes" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "testing" +) + +// Issue 36021: verify that text/template doesn't prevent the linker from removing +// unused methods. +func TestLinkerGC(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + testenv.MustHaveGoBuild(t) + const prog = `package main + +import ( + _ "text/template" +) + +type T struct{} + +func (t *T) Unused() { println("THIS SHOULD BE ELIMINATED") } +func (t *T) Used() {} + +var sink *T + +func main() { + var t T + sink = &t + t.Used() +} +` + td, err := ioutil.TempDir("", "text_template_TestDeadCodeElimination") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(td) + + if err := ioutil.WriteFile(filepath.Join(td, "x.go"), []byte(prog), 0644); err != nil { + t.Fatal(err) + } + cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "x.exe", "x.go") + cmd.Dir = td + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("go build: %v, %s", err, out) + } + slurp, err := ioutil.ReadFile(filepath.Join(td, "x.exe")) + if err != nil { + t.Fatal(err) + } + if bytes.Contains(slurp, []byte("THIS SHOULD BE ELIMINATED")) { + t.Error("binary contains code that should be deadcode eliminated") + } +} diff --git a/libgo/go/text/template/multi_test.go b/libgo/go/text/template/multi_test.go index 5769470..34d2378 100644 --- a/libgo/go/text/template/multi_test.go +++ b/libgo/go/text/template/multi_test.go @@ -242,7 +242,7 @@ func TestAddParseTree(t *testing.T) { t.Fatal(err) } // Add a new parse tree. - tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins) + tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins()) if err != nil { t.Fatal(err) } @@ -359,6 +359,7 @@ func TestEmptyTemplate(t *testing.T) { in string want string }{ + {[]string{"x", "y"}, "", "y"}, {[]string{""}, "once", ""}, {[]string{"", ""}, "twice", ""}, {[]string{"{{.}}", "{{.}}"}, "twice", "twice"}, diff --git a/libgo/go/text/template/parse/node.go b/libgo/go/text/template/parse/node.go index 1c116ea..dddc775 100644 --- a/libgo/go/text/template/parse/node.go +++ b/libgo/go/text/template/parse/node.go @@ -349,7 +349,7 @@ func (i *IdentifierNode) Copy() Node { return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos) } -// AssignNode holds a list of variable names, possibly with chained field +// VariableNode holds a list of variable names, possibly with chained field // accesses. The dollar sign is part of the (first) name. type VariableNode struct { NodeType diff --git a/libgo/go/text/template/template.go b/libgo/go/text/template/template.go index e0c09620..ec26eb4 100644 --- a/libgo/go/text/template/template.go +++ b/libgo/go/text/template/template.go @@ -198,7 +198,7 @@ func (t *Template) Lookup(name string) *Template { func (t *Template) Parse(text string) (*Template, error) { t.init() t.muFuncs.RLock() - trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) + trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins()) t.muFuncs.RUnlock() if err != nil { return nil, err |